From 2ed310470f134c92379fb177e7dd8116d3083b49 Mon Sep 17 00:00:00 2001 From: Kudo Chien Date: Fri, 25 Feb 2022 00:35:17 +0800 Subject: [PATCH] [android][ios] Upgrade react-native to 0.67.2 in Expo Go (#16400) # Why upgrade for sdk 45 # How ## upgrade react native fork code in https://github.com/expo/react-native/tree/sdk-45 basically cherry pick most necessary commits from sdk-44 branch but not these two: 1. https://github.com/expo/react-native/commit/125b36ac6d76e702bea4d7c41a8a402d68cdb1f1: because ExpoKit is already deprecated. 2. https://github.com/expo/react-native/commit/82d3ff5218cc576b5c7ea44c7adbf8b5aa5d985c: [use the official way to set metro server address](https://github.com/facebook/react-native/commit/e2b5b6504cb4dee8f2ec67f1d3a410801e1ca09a) ## migrate project setup basically reference from: https://react-native-community.github.io/upgrade-helper/?from=0.64.2&to=0.67.2 ## other build errors or launch errors just to deal with these mess one by one. # Test Plan - [android] unversioned expo-go build + launch + load unversioned NCL - [ios] unversioned expo-go build + launch + load unversioned NCL --- .github/workflows/android-unit-tests.yml | 10 +- .github/workflows/client-android.yml | 8 +- .github/workflows/development-client.yml | 8 +- .github/workflows/dogfooding-clients.yml | 8 +- .github/workflows/shell-app-android.yml | 10 +- android/ReactAndroid/Android-prebuilt.mk | 85 +- android/ReactAndroid/README.md | 4 +- android/ReactAndroid/build.gradle | 219 +- android/ReactAndroid/gradle.properties | 12 +- android/ReactAndroid/proguard-rules.pro | 8 +- android/ReactAndroid/release.gradle | 142 - .../src/androidTest/buck-runner/BUCK | 1 + .../java/com/facebook/react/testing/BUCK | 3 +- .../ReactAppInstrumentationTestCase.java | 2 +- .../facebook/react/testing/idledetection/BUCK | 3 +- .../idledetection/ReactIdleDetectionUtil.java | 2 +- .../com/facebook/react/testing/network/BUCK | 1 + .../java/com/facebook/react/testing/rule/BUCK | 11 +- .../java/com/facebook/react/tests/BUCK | 3 +- .../react/tests/DatePickerDialogTestCase.java | 172 - .../react/tests/ReactPickerTestCase.java | 196 - .../react/tests/TextInputTestCase.java | 3 +- .../java/com/facebook/react/tests/core/BUCK | 11 +- android/ReactAndroid/src/androidTest/js/BUCK | 26 + .../js/DatePickerDialogTestModule.js | 45 - .../androidTest/js/PickerAndroidTestModule.js | 89 - .../src/androidTest/js/TestBundle.js | 12 - .../src/androidTest/js/UIManagerTestModule.js | 13 +- .../src/main/java/com/facebook/BUCK | 1 + .../facebook/debug/debugoverlay/model/BUCK | 1 + .../main/java/com/facebook/debug/holder/BUCK | 1 + .../main/java/com/facebook/debug/tags/BUCK | 1 + .../hermes/instrumentation/Android.mk | 4 +- .../com/facebook/hermes/instrumentation/BUCK | 5 +- .../facebook/hermes/reactexecutor/Android.mk | 8 +- .../com/facebook/hermes/reactexecutor/BUCK | 2 + .../hermes/reactexecutor/HermesExecutor.java | 22 +- .../reactexecutor/HermesExecutorFactory.java | 2 +- .../hermes/reactexecutor/RuntimeConfig.java | 6 - .../java/com/facebook/hermes/unicode/BUCK | 1 + .../src/main/java/com/facebook/perftest/BUCK | 1 + .../com/facebook/proguard/annotations/BUCK | 5 +- .../annotations/proguard_annotations.pro | 1 + .../src/main/java/com/facebook/react/BUCK | 4 +- .../facebook/react/CoreModulesPackage.java | 5 +- .../com/facebook/react/ReactActivity.java | 8 + .../facebook/react/ReactActivityDelegate.java | 13 +- .../ReactAndroidHWInputDeviceHelper.java | 5 + .../com/facebook/react/ReactFragment.java | 8 +- .../facebook/react/ReactFragmentActivity.java | 15 - .../facebook/react/ReactInstanceManager.java | 285 +- .../react/ReactInstanceManagerBuilder.java | 60 +- .../com/facebook/react/ReactNativeHost.java | 40 +- ...eactPackageTurboModuleManagerDelegate.java | 24 +- .../com/facebook/react/ReactRootView.java | 137 +- .../java/com/facebook/react/animated/BUCK | 3 +- .../react/animated/NativeAnimatedModule.java | 12 +- .../animated/NativeAnimatedNodesManager.java | 99 +- .../com/facebook/react/bridge/Arguments.java | 4 + .../main/java/com/facebook/react/bridge/BUCK | 2 + .../react/bridge/BackgroundExecutor.java | 5 +- .../facebook/react/bridge/BaseJavaModule.java | 12 +- .../react/bridge/CatalystInstance.java | 2 + .../react/bridge/CatalystInstanceImpl.java | 25 +- .../react/bridge/CxxModuleWrapperBase.java | 5 +- .../react/bridge/JSIModuleRegistry.java | 4 + .../react/bridge/JavaModuleWrapper.java | 37 + .../facebook/react/bridge/ModuleHolder.java | 2 +- .../facebook/react/bridge/NativeModule.java | 9 +- .../react/bridge/NativeModuleRegistry.java | 26 + .../facebook/react/bridge/ReactContext.java | 55 +- .../bridge/ReactContextBaseJavaModule.java | 5 +- .../react/bridge/ReactCxxErrorHandler.java | 39 + .../react/bridge/ReactMarkerConstants.java | 16 +- ...ion.java => ReactSoftExceptionLogger.java} | 11 +- .../react/bridge/ReadableNativeMap.java | 54 +- .../react/bridge/RuntimeScheduler.java | 21 + .../facebook/react/bridge/SoftAssertions.java | 15 +- .../com/facebook/react/bridge/UIManager.java | 39 +- .../main/java/com/facebook/react/common/BUCK | 5 +- .../react/common/SurfaceDelegate.java | 40 + .../react/common/SurfaceDelegateFactory.java | 24 + .../react/common/build/ReactBuildConfig.java | 1 + .../com/facebook/react/common/mapbuffer/BUCK | 29 + .../common/mapbuffer/ReadableMapBuffer.java | 347 ++ .../mapbuffer/ReadableMapBufferSoLoader.java | 33 + .../react/common/mapbuffer/jni/Android.mk | 39 + .../facebook/react/common/mapbuffer/jni/BUCK | 28 + .../jni/react/common/mapbuffer/OnLoad.cpp | 15 + .../common/mapbuffer/ReadableMapBuffer.cpp | 76 + .../common/mapbuffer/ReadableMapBuffer.h | 51 + .../com/facebook/react/common/network/BUCK | 1 + .../main/java/com/facebook/react/config/BUCK | 4 +- .../react/config/ReactFeatureFlags.java | 100 +- .../java/com/facebook/react/devsupport/BUCK | 5 +- .../devsupport/BridgeDevSupportManager.java | 202 + .../DefaultDevSupportManagerFactory.java | 102 + .../devsupport/DevLoadingViewController.java | 4 +- .../react/devsupport/DevServerHelper.java | 56 +- .../devsupport/DevSupportManagerBase.java | 489 +-- .../devsupport/DevSupportManagerFactory.java | 79 +- .../devsupport/DevSupportManagerImpl.java | 82 - .../devsupport/DisabledDevSupportManager.java | 26 +- .../LogBoxDialogSurfaceDelegate.java | 92 + .../react/devsupport/LogBoxModule.java | 79 +- .../react/devsupport/PackagerStatusCheck.java | 100 + ...elper.java => ReactInstanceDevHelper.java} | 5 +- .../react/devsupport/RedBoxHandler.java | 18 +- .../interfaces/BundleLoadCallback.java} | 9 +- .../interfaces/DevSupportManager.java | 21 +- .../devsupport/interfaces/ErrorType.java | 23 + .../main/java/com/facebook/react/fabric/BUCK | 3 +- .../com/facebook/react/fabric/Binding.java | 20 +- .../react/fabric/FabricComponents.java | 7 +- .../react/fabric/FabricJSIModuleProvider.java | 98 +- .../react/fabric/FabricUIManager.java | 1121 +++-- .../react/fabric/StateWrapperImpl.java | 61 +- .../facebook/react/fabric/SurfaceHandler.java | 50 + .../react/fabric/SurfaceHandlerBinding.java | 131 + .../fabric/events/EventEmitterWrapper.java | 46 +- .../fabric/events/FabricEventEmitter.java | 78 +- .../com/facebook/react/fabric/jni/Android.mk | 10 +- .../react/fabric/jni/AsyncEventBeatV2.cpp | 69 + .../react/fabric/jni/AsyncEventBeatV2.h | 40 + .../java/com/facebook/react/fabric/jni/BUCK | 13 +- .../com/facebook/react/fabric/jni/Binding.cpp | 995 ++--- .../com/facebook/react/fabric/jni/Binding.h | 53 +- .../fabric/jni/CoreComponentsRegistry.cpp | 9 +- .../react/fabric/jni/EventEmitterWrapper.cpp | 44 +- .../react/fabric/jni/EventEmitterWrapper.h | 7 +- .../com/facebook/react/fabric/jni/OnLoad.cpp | 2 + .../react/fabric/jni/StateWrapperImpl.cpp | 43 +- .../react/fabric/jni/StateWrapperImpl.h | 4 +- .../fabric/jni/SurfaceHandlerBinding.cpp | 122 + .../react/fabric/jni/SurfaceHandlerBinding.h | 68 + .../fabric/mounting/MountItemDispatcher.java | 414 ++ .../fabric/mounting/MountingManager.java | 929 ++--- .../mounting/SurfaceMountingManager.java | 1023 +++++ .../mounting/mountitems/BatchMountItem.java | 117 - .../mounting/mountitems/CreateMountItem.java | 68 - .../DispatchIntCommandMountItem.java | 11 +- .../DispatchStringCommandMountItem.java | 11 +- .../mounting/mountitems/InsertMountItem.java | 47 - .../mountitems/IntBufferBatchMountItem.java | 81 +- .../fabric/mounting/mountitems/MountItem.java | 4 + .../mountitems/PreAllocateViewMountItem.java | 44 +- .../RemoveDeleteMultiMountItem.java | 90 - .../mountitems/SendAccessibilityEvent.java | 15 +- .../UpdateEventEmitterMountItem.java | 33 - .../mountitems/UpdateLayoutMountItem.java | 90 - .../mountitems/UpdatePaddingMountItem.java | 53 - .../mountitems/UpdatePropsMountItem.java | 42 - .../mountitems/UpdateStateMountItem.java | 43 - .../com/facebook/react/jscexecutor/Android.mk | 4 +- .../java/com/facebook/react/jscexecutor/BUCK | 4 +- .../react/jscexecutor/JSCExecutor.java | 7 +- .../main/java/com/facebook/react/jstasks/BUCK | 1 + .../react/jstasks/HeadlessJsTaskContext.java | 6 +- .../facebook/react/module/annotations/BUCK | 1 + .../java/com/facebook/react/module/model/BUCK | 1 + .../AccessibilityInfoModule.java | 25 +- .../react/modules/accessibilityinfo/BUCK | 3 +- .../modules/appearance/AppearanceModule.java | 11 +- .../facebook/react/modules/appearance/BUCK | 3 +- .../facebook/react/modules/appregistry/BUCK | 1 + .../modules/appstate/AppStateModule.java | 12 +- .../com/facebook/react/modules/appstate/BUCK | 3 +- .../java/com/facebook/react/modules/blob/BUCK | 3 +- .../react/modules/blob/BlobProvider.java | 42 +- .../react/modules/blob/jni/Android.mk | 4 +- .../com/facebook/react/modules/blob/jni/BUCK | 6 - .../react/modules/blob/jni/BlobCollector.cpp | 1 + .../facebook/react/modules/bundleloader/BUCK | 3 +- .../com/facebook/react/modules/camera/BUCK | 3 +- .../com/facebook/react/modules/clipboard/BUCK | 6 +- .../modules/clipboard/ClipboardModule.java | 17 +- .../com/facebook/react/modules/common/BUCK | 1 + .../java/com/facebook/react/modules/core/BUCK | 3 +- .../modules/core/ExceptionsManagerModule.java | 23 +- ...ager.java => JavaScriptTimerExecutor.java} | 2 +- .../react/modules/core/JavaTimerManager.java | 14 +- .../react/modules/core/TimingModule.java | 11 +- .../facebook/react/modules/datepicker/BUCK | 26 - .../datepicker/DatePickerDialogFragment.java | 137 - .../datepicker/DatePickerDialogModule.java | 147 - .../DismissableDatePickerDialog.java | 117 - .../modules/debug/AnimationsDebugModule.java | 2 +- .../com/facebook/react/modules/debug/BUCK | 4 +- .../facebook/react/modules/deviceinfo/BUCK | 3 +- .../modules/deviceinfo/DeviceInfoModule.java | 27 +- .../com/facebook/react/modules/dialog/BUCK | 3 +- .../react/modules/dialog/DialogModule.java | 13 +- .../com/facebook/react/modules/fabric/BUCK | 1 + .../com/facebook/react/modules/fresco/BUCK | 1 + .../react/modules/fresco/FrescoModule.java | 10 + .../facebook/react/modules/i18nmanager/BUCK | 4 +- .../i18nmanager/I18nManagerModule.java | 32 +- .../com/facebook/react/modules/image/BUCK | 3 +- .../com/facebook/react/modules/intent/BUCK | 3 +- .../react/modules/intent/IntentModule.java | 42 +- .../com/facebook/react/modules/network/BUCK | 3 +- .../network/ForwardingCookieHandler.java | 5 +- .../modules/network/NetworkingModule.java | 2 +- .../modules/network/OkHttpClientProvider.java | 19 +- .../facebook/react/modules/permissions/BUCK | 3 +- .../com/facebook/react/modules/share/BUCK | 3 +- .../com/facebook/react/modules/sound/BUCK | 3 +- .../com/facebook/react/modules/statusbar/BUCK | 3 +- .../modules/storage/AsyncStorageModule.java | 2 +- .../com/facebook/react/modules/storage/BUCK | 3 +- .../facebook/react/modules/systeminfo/BUCK | 6 +- .../systeminfo/ReactNativeVersion.java | 4 +- .../com/facebook/react/modules/toast/BUCK | 3 +- .../com/facebook/react/modules/vibration/BUCK | 3 +- .../com/facebook/react/modules/websocket/BUCK | 3 +- .../facebook/react/packagerconnection/BUCK | 1 + .../ReconnectingWebSocket.java | 20 +- .../processing/ReactPropertyProcessor.java | 2 +- .../com/facebook/react/reactperflogger/BUCK | 1 + .../react/reactperflogger/jni/Android.mk | 2 +- .../facebook/react/reactperflogger/jni/BUCK | 6 - .../main/java/com/facebook/react/shell/BUCK | 3 +- .../react/shell/MainReactPackage.java | 9 - .../main/java/com/facebook/react/surface/BUCK | 1 + .../main/java/com/facebook/react/touch/BUCK | 1 + .../com/facebook/react/turbomodule/core/BUCK | 3 +- .../turbomodule/core/TurboModuleManager.java | 21 +- .../core/TurboModuleManagerDelegate.java | 3 + .../core/TurboModulePerfLogger.java | 2 + .../react/turbomodule/core/interfaces/BUCK | 1 + .../core/interfaces/TurboModule.java | 8 +- .../react/turbomodule/core/jni/Android.mk | 8 +- .../facebook/react/turbomodule/core/jni/BUCK | 13 +- .../jni/ReactCommon/TurboModuleManager.cpp | 201 +- .../core/jni/ReactCommon/TurboModuleManager.h | 19 +- .../java/com/facebook/react/uimanager/BUCK | 7 +- .../react/uimanager/BaseViewManager.java | 16 +- .../uimanager/ComponentNameResolver.java | 17 + .../ComponentNameResolverManager.java | 41 + .../react/uimanager/DisplayMetricsHolder.java | 35 +- .../uimanager/FabricViewStateManager.java | 24 +- .../react/uimanager/JSTouchDispatcher.java | 29 +- .../uimanager/NativeViewHierarchyManager.java | 14 +- .../NativeViewHierarchyOptimizer.java | 23 +- .../react/uimanager/OnLayoutEvent.java | 23 +- .../facebook/react/uimanager/PixelUtil.java | 7 +- .../uimanager/ReactAccessibilityDelegate.java | 26 +- .../ReactClippingProhibitedView.java | 26 + .../ReactClippingViewGroupHelper.java | 54 +- .../react/uimanager/ReactOverflowView.java | 25 + .../facebook/react/uimanager/ReactRoot.java | 14 + .../react/uimanager/RootViewUtil.java | 18 + .../react/uimanager/StateWrapper.java | 28 +- .../react/uimanager/ThemedReactContext.java | 42 +- .../react/uimanager/TouchTargetHelper.java | 230 +- .../react/uimanager/UIImplementation.java | 51 +- .../uimanager/UIImplementationProvider.java | 48 +- .../react/uimanager/UIManagerHelper.java | 86 +- .../react/uimanager/UIManagerModule.java | 109 +- .../UIManagerModuleConstantsHelper.java | 3 +- .../react/uimanager/UIViewOperationQueue.java | 19 +- .../facebook/react/uimanager/ViewManager.java | 28 +- .../react/uimanager/ViewManagerRegistry.java | 4 +- .../react/uimanager/ViewManagerResolver.java | 26 + .../uimanager/ViewManagersPropertyCache.java | 17 +- .../facebook/react/uimanager/annotations/BUCK | 1 + .../com/facebook/react/uimanager/common/BUCK | 2 +- .../events/BlackHoleEventDispatcher.java | 3 + .../events/ContentSizeChangeEvent.java | 11 +- .../react/uimanager/events/Event.java | 139 +- .../uimanager/events/EventCategoryDef.java | 42 + .../uimanager/events/EventDispatcher.java | 3 + .../uimanager/events/EventDispatcherImpl.java | 17 +- .../events/LockFreeEventDispatcherImpl.java | 236 ++ .../uimanager/events/RCTEventEmitter.java | 17 + .../events/RCTModernEventEmitter.java | 33 + .../uimanager/events/ReactEventEmitter.java | 113 +- .../react/uimanager/events/TouchEvent.java | 108 +- .../uimanager/events/TouchEventType.java | 31 +- .../react/uimanager/events/TouchesHelper.java | 122 +- .../interfaces}/BUCK | 9 +- .../BaseViewManagerDelegate.java | 0 .../BaseViewManagerInterface.java | 0 .../uimanager/{ => interfaces}/FloatUtil.java | 0 .../uimanager/{ => interfaces}/Spacing.java | 0 .../{ => interfaces}/ViewManagerDelegate.java | 0 .../uimanager/{ => interfaces}/ViewProps.java | 12 +- .../facebook/react/uimanager/jni/Android.mk | 37 + .../com/facebook/react/uimanager/jni/BUCK | 35 + .../jni/ComponentNameResolverManager.cpp | 77 + .../jni/ComponentNameResolverManager.h | 54 + .../facebook/react/uimanager/jni/OnLoad.cpp | 16 + .../com/facebook/react/uimanager/util/BUCK | 1 + .../main/java/com/facebook/react/util/BUCK | 1 + .../ActivityIndicatorViewManagerDelegate.java | 42 - ...ActivityIndicatorViewManagerInterface.java | 20 - .../AndroidDialogPickerManagerDelegate.java | 48 - .../AndroidDialogPickerManagerInterface.java | 23 - .../AndroidDrawerLayoutManagerDelegate.java | 61 - .../AndroidDrawerLayoutManagerInterface.java | 24 - .../AndroidDropdownPickerManagerDelegate.java | 48 - ...AndroidDropdownPickerManagerInterface.java | 22 - .../AndroidProgressBarManagerDelegate.java | 51 - .../AndroidProgressBarManagerInterface.java | 23 - ...roidSwipeRefreshLayoutManagerDelegate.java | 58 - ...oidSwipeRefreshLayoutManagerInterface.java | 24 - .../AndroidSwitchManagerDelegate.java | 67 - .../AndroidSwitchManagerInterface.java | 26 - .../AndroidViewPagerManagerDelegate.java | 57 - .../AndroidViewPagerManagerInterface.java | 23 - .../DatePickerManagerDelegate.java | 62 - .../DatePickerManagerInterface.java | 25 - .../InputAccessoryViewManagerDelegate.java | 33 - .../InputAccessoryViewManagerInterface.java | 17 - .../MaskedViewManagerDelegate.java | 26 - .../MaskedViewManagerInterface.java | 16 - .../ModalHostViewManagerDelegate.java | 54 - .../ModalHostViewManagerInterface.java | 25 - .../ProgressViewManagerDelegate.java | 49 - .../ProgressViewManagerInterface.java | 23 - .../PullToRefreshViewManagerDelegate.java | 51 - .../PullToRefreshViewManagerInterface.java | 21 - .../SafeAreaViewManagerDelegate.java | 32 - .../SafeAreaViewManagerInterface.java | 16 - .../SegmentedControlManagerDelegate.java | 52 - .../SegmentedControlManagerInterface.java | 24 - .../viewmanagers/SliderManagerDelegate.java | 73 - .../viewmanagers/SliderManagerInterface.java | 31 - .../viewmanagers/SwitchManagerDelegate.java | 63 - .../viewmanagers/SwitchManagerInterface.java | 24 - ...nimplementedNativeViewManagerDelegate.java | 32 - ...implementedNativeViewManagerInterface.java | 17 - .../react/viewmanagers/jni/Android.mk | 29 - .../renderer/components/rncore/Props.cpp | 191 - .../java/com/facebook/react/views/common/BUCK | 1 + .../java/com/facebook/react/views/drawer/BUCK | 5 +- .../drawer/ReactDrawerLayoutManager.java | 25 +- .../drawer/events/DrawerClosedEvent.java | 19 +- .../drawer/events/DrawerOpenedEvent.java | 19 +- .../views/drawer/events/DrawerSlideEvent.java | 20 +- .../events/DrawerStateChangedEvent.java | 20 +- .../java/com/facebook/react/views/image/BUCK | 2 + .../react/views/image/ImageLoadEvent.java | 64 +- .../image/ReactCallerContextFactory.java | 4 +- .../react/views/image/ReactImageManager.java | 19 +- .../react/views/image/ReactImageView.java | 101 +- .../com/facebook/react/views/imagehelper/BUCK | 2 + .../react/views/imagehelper/ImageSource.java | 17 + .../java/com/facebook/react/views/modal/BUCK | 6 +- .../views/modal/ReactModalHostManager.java | 16 +- .../react/views/modal/ReactModalHostView.java | 55 +- .../react/views/modal/RequestCloseEvent.java | 16 +- .../facebook/react/views/modal/ShowEvent.java | 16 +- .../java/com/facebook/react/views/picker/BUCK | 29 - .../picker/ReactDialogPickerManager.java | 51 - .../picker/ReactDropdownPickerManager.java | 50 - .../react/views/picker/ReactPicker.java | 196 - .../views/picker/ReactPickerAdapter.java | 95 - .../react/views/picker/ReactPickerItem.java | 43 - .../views/picker/ReactPickerManager.java | 97 - .../picker/events/PickerItemSelectEvent.java | 40 - .../com/facebook/react/views/progressbar/BUCK | 5 +- .../java/com/facebook/react/views/scroll/BUCK | 2 + .../ReactHorizontalScrollContainerView.java | 35 +- .../scroll/ReactHorizontalScrollView.java | 296 +- .../ReactHorizontalScrollViewManager.java | 18 +- .../react/views/scroll/ReactScrollView.java | 223 +- .../views/scroll/ReactScrollViewHelper.java | 43 +- .../views/scroll/ReactScrollViewManager.java | 20 +- .../react/views/scroll/ScrollEvent.java | 55 +- .../java/com/facebook/react/views/slider/BUCK | 5 +- .../react/views/slider/ReactSliderEvent.java | 5 - .../views/slider/ReactSliderManager.java | 49 +- .../slider/ReactSlidingCompleteEvent.java | 29 +- .../facebook/react/views/swiperefresh/BUCK | 5 +- .../views/swiperefresh/RefreshEvent.java | 16 +- .../SwipeRefreshLayoutManager.java | 25 +- .../com/facebook/react/views/switchview/BUCK | 5 +- .../react/views/switchview/ReactSwitch.java | 13 + .../views/switchview/ReactSwitchEvent.java | 22 +- .../views/switchview/ReactSwitchManager.java | 16 +- .../java/com/facebook/react/views/text/BUCK | 2 + .../react/views/text/CustomStyleSpan.java | 14 +- .../views/text/ReactBaseTextShadowNode.java | 15 +- .../react/views/text/ReactClickableSpan.java | 3 +- .../react/views/text/ReactFontManager.java | 103 +- .../text/ReactTextAnchorViewManager.java | 4 - .../react/views/text/ReactTextShadowNode.java | 36 +- .../react/views/text/ReactTextView.java | 21 +- .../views/text/ReactTextViewManager.java | 49 +- .../react/views/text/ReactTypefaceUtils.java | 100 +- .../react/views/text/TextAttributeProps.java | 369 +- .../react/views/text/TextLayoutManager.java | 115 +- .../text/TextLayoutManagerMapBuffer.java | 616 +++ .../react/views/text/TypefaceStyle.java | 72 + .../react/views/text/frescosupport/BUCK | 1 + .../com/facebook/react/views/textinput/BUCK | 2 + .../ReactContentSizeChangedEvent.java | 17 +- .../react/views/textinput/ReactEditText.java | 54 +- .../ReactEditTextInputConnectionWrapper.java | 14 +- .../textinput/ReactTextChangedEvent.java | 16 +- .../textinput/ReactTextInputBlurEvent.java | 16 +- .../ReactTextInputEndEditingEvent.java | 16 +- .../views/textinput/ReactTextInputEvent.java | 17 +- .../textinput/ReactTextInputFocusEvent.java | 26 +- .../ReactTextInputKeyPressEvent.java | 29 +- .../textinput/ReactTextInputManager.java | 224 +- .../ReactTextInputSelectionEvent.java | 17 +- .../ReactTextInputSubmitEditingEvent.java | 26 +- .../react/views/unimplementedview/BUCK | 1 + .../java/com/facebook/react/views/view/BUCK | 1 + .../facebook/react/views/view/CanvasUtil.java | 92 + .../react/views/view/ReactDrawableHelper.java | 6 +- .../view/ReactViewBackgroundDrawable.java | 47 +- .../react/views/view/ReactViewGroup.java | 145 +- .../react/views/view/ReactViewManager.java | 4 +- .../react/views/view/ViewGroupClickEvent.java | 15 +- .../src/main/java/com/facebook/systrace/BUCK | 1 + .../main/java/com/facebook/yoga/YogaNode.java | 20 +- .../com/facebook/yoga/YogaNodeJNIBase.java | 26 +- .../java/com/facebook/yoga/YogaProps.java | 151 + .../ReactAndroid/src/main/jni/Application.mk | 2 +- .../src/main/jni/first-party/fb/BUCK | 4 + .../jni/first-party/fb/include/fb/RefPtr.h | 2 +- .../fb/include/fb/StaticInitialized.h | 2 +- .../first-party/yogajni/jni/YGJNIVanilla.cpp | 9 +- .../first-party/yogajni/jni/YGJTypesVanilla.h | 12 +- .../src/main/jni/react/jni/Android.mk | 15 +- .../ReactAndroid/src/main/jni/react/jni/BUCK | 14 +- .../jni/react/jni/CatalystInstanceImpl.cpp | 113 +- .../main/jni/react/jni/CatalystInstanceImpl.h | 21 +- .../jni/react/jni/JReactCxxErrorHandler.cpp | 18 + .../jni/react/jni/JReactCxxErrorHandler.h} | 16 +- .../react/jni/JReactSoftExceptionLogger.cpp | 21 + .../jni/react/jni/JReactSoftExceptionLogger.h | 28 + .../main/jni/react/jni/JRuntimeScheduler.cpp} | 15 +- .../main/jni/react/jni/JRuntimeScheduler.h | 30 + .../src/main/jni/react/jni/JSLoader.cpp | 2 +- .../main/jni/react/jni/JavaModuleWrapper.cpp | 94 - .../main/jni/react/jni/JavaModuleWrapper.h | 30 - .../src/main/jni/react/jni/ProxyExecutor.h | 1 + .../main/jni/react/jni/ReadableNativeMap.cpp | 13 +- .../src/main/jni/react/perftests/BUCK | 4 - .../src/main/jni/third-party/boost/Android.mk | 9 +- .../boost/asm/arm/jump_arm_aapcs_elf_gas.S | 86 + .../boost/asm/arm/make_arm_aapcs_elf_gas.S | 79 + .../boost/asm/arm/ontop_arm_aapcs_elf_gas.S | 91 + .../asm/arm64/jump_arm64_aapcs_elf_gas.S | 114 + .../asm/arm64/make_arm64_aapcs_elf_gas.S | 85 + .../asm/arm64/ontop_arm64_aapcs_elf_gas.S | 113 + .../boost/asm/x86/jump_i386_sysv_elf_gas.S | 78 + .../boost/asm/x86/make_i386_sysv_elf_gas.S | 106 + .../boost/asm/x86/ontop_i386_sysv_elf_gas.S | 85 + .../asm/x86_64/jump_x86_64_sysv_elf_gas.S | 76 + .../asm/x86_64/make_x86_64_sysv_elf_gas.S | 81 + .../asm/x86_64/ontop_x86_64_sysv_elf_gas.S | 79 + .../src/main/jni/third-party/fmt/Android.mk | 15 + .../src/main/jni/third-party/folly/Android.mk | 52 +- .../main/jni/third-party/libevent/Android.mk | 36 + .../third-party/libevent/evconfig-private.h | 49 + .../jni/third-party/libevent/event-config.h | 340 ++ .../java/com/facebook/common/logging/BUCK | 1 + .../src/main/libraries/fbjni/BUCK | 4 +- .../libraries/fresco/fresco-react-native/BUCK | 107 +- .../soloader/java/com/facebook/soloader/BUCK | 10 +- .../main/res/devsupport/values/strings.xml | 3 +- .../uimanager/values/strings_unlocalized.xml | 12 +- .../main/third-party/android/androidx/BUCK | 65 +- .../main/third-party/android/support/v4/BUCK | 685 --- .../android/support/v7/appcompat/BUCK | 62 - .../support/v7/appcompat/aar-unpacker.py | 20 - .../src/main/third-party/java/asm/BUCK | 1 + .../src/main/third-party/java/fest/BUCK | 1 + .../third-party/java/infer-annotations/BUCK | 4 +- .../src/main/third-party/java/junit/BUCK | 1 + .../src/main/third-party/java/mockito/BUCK | 1 + .../src/main/third-party/java/mockito2/BUCK | 1 + .../src/main/third-party/java/okhttp/BUCK | 38 +- .../src/main/third-party/java/okio/BUCK | 20 +- .../java/robolectric/{4.4 => }/BUCK | 1 + .../src/main/third-party/java/sqlite/BUCK | 1 + .../third-party/java/testing-support-lib/BUCK | 31 - .../src/main/third-party/kotlin/BUCK | 91 + .../java/com/facebook/common/logging/BUCK | 1 + .../java/com/facebook/react/RootViewTest.java | 54 +- .../NativeAnimatedNodeTraversalTest.java | 2 +- .../test/java/com/facebook/react/bridge/BUCK | 3 +- .../react/bridge/ReactTestHelper.java | 1 + .../JSDebuggerWebSocketClientTest.java | 4 +- .../test/java/com/facebook/react/modules/BUCK | 1 + .../clipboard/ClipboardModuleTest.java | 4 +- .../deviceinfo/DeviceInfoModuleTest.java | 168 + .../modules/dialog/DialogModuleTest.java | 2 +- .../modules/network/NetworkingModuleTest.java | 20 +- .../modules/timing/TimingModuleTest.java | 2 +- .../react/uimanager/BaseViewManagerTest.java | 32 + .../uimanager/SimpleViewPropertyTest.java | 10 +- .../views/image/ReactImagePropertyTest.java | 8 + .../textinput/ReactTextInputPropertyTest.java | 4 + .../test/java/org/mockito/configuration/BUCK | 1 + android/ReactCommon/React-Fabric.podspec | 75 +- android/ReactCommon/ReactCommon.podspec | 13 +- android/ReactCommon/better/Android.mk | 2 +- android/ReactCommon/better/BUCK | 8 +- android/ReactCommon/callinvoker/Android.mk | 2 +- android/ReactCommon/callinvoker/BUCK | 10 +- .../callinvoker/React-callinvoker.podspec | 6 +- android/ReactCommon/cxxreact/Android.mk | 3 +- android/ReactCommon/cxxreact/BUCK | 21 +- .../ReactCommon/cxxreact/CxxNativeModule.cpp | 28 + .../ReactCommon/cxxreact/CxxNativeModule.h | 9 + android/ReactCommon/cxxreact/Instance.cpp | 34 +- android/ReactCommon/cxxreact/Instance.h | 1 + android/ReactCommon/cxxreact/JSBundleType.cpp | 5 + android/ReactCommon/cxxreact/JSBundleType.h | 1 + .../cxxreact/JsArgumentHelpers-inl.h | 6 +- .../ReactCommon/cxxreact/JsArgumentHelpers.h | 4 +- android/ReactCommon/cxxreact/MethodCall.cpp | 4 +- .../ReactCommon/cxxreact/ModuleRegistry.cpp | 5 + .../ReactCommon/cxxreact/NativeToJsBridge.cpp | 21 - .../ReactCommon/cxxreact/NativeToJsBridge.h | 6 - .../cxxreact/React-cxxreact.podspec | 11 +- .../ReactCommon/cxxreact/ReactNativeVersion.h | 4 +- .../ReactCommon/cxxreact/SystraceSection.h | 4 +- .../cxxreact/re_worker_requirements | 6 - .../ReactCommon/hermes/React-hermes.podspec | 53 + .../ReactCommon/hermes/executor/Android.mk | 8 +- .../ReactCommon/hermes/inspector/Android.mk | 3 +- android/ReactCommon/hermes/inspector/BUCK | 13 +- .../hermes/inspector/Inspector.cpp | 5 +- .../ReactCommon/hermes/inspector/Inspector.h | 11 +- .../hermes/inspector/InspectorState.cpp | 14 +- .../hermes/inspector/chrome/Connection.cpp | 115 + .../hermes/inspector/chrome/MessageTypes.cpp | 216 +- .../hermes/inspector/chrome/MessageTypes.h | 116 +- .../hermes/inspector/chrome/cli/main.cpp | 11 +- .../chrome/tests/ConnectionTests.cpp | 144 + .../hermes/inspector/tools/message_types.txt | 4 + .../tools/msggen/__tests__/CommandTest.js | 4 +- .../tools/msggen/__tests__/EventTest.js | 4 +- .../tools/msggen/__tests__/GraphTest.js | 4 +- .../msggen/__tests__/HeaderWriterTest.js | 4 +- .../__tests__/ImplementationWriterTest.js | 4 +- .../tools/msggen/__tests__/PropertyTest.js | 4 +- .../tools/msggen/__tests__/TypeTest.js | 4 +- .../inspector/tools/msggen/package.json | 13 +- .../inspector/tools/msggen/src/Command.js | 2 - .../inspector/tools/msggen/src/Converters.js | 2 - .../inspector/tools/msggen/src/Event.js | 2 - .../inspector/tools/msggen/src/Graph.js | 2 - .../tools/msggen/src/HeaderWriter.js | 2 - .../tools/msggen/src/ImplementationWriter.js | 2 - .../inspector/tools/msggen/src/Property.js | 3 +- .../inspector/tools/msggen/src/TestHelpers.js | 4 +- .../hermes/inspector/tools/msggen/src/Type.js | 2 - .../inspector/tools/msggen/src/index.js | 4 +- .../hermes/inspector/tools/msggen/yarn.lock | 3665 ++++++++++------- android/ReactCommon/jsi/Android.mk | 8 +- android/ReactCommon/jsi/BUCK | 13 - android/ReactCommon/jsi/JSCRuntime.cpp | 7 + android/ReactCommon/jsi/React-jsi.podspec | 10 +- android/ReactCommon/jsi/jsi/CMakeLists.txt | 2 +- android/ReactCommon/jsi/jsi/decorator.h | 15 + android/ReactCommon/jsi/jsi/instrumentation.h | 13 + android/ReactCommon/jsi/jsi/jsi.cpp | 3 + android/ReactCommon/jsi/jsi/jsi.h | 35 +- android/ReactCommon/jsi/jsi/jsilib-posix.cpp | 12 +- android/ReactCommon/jsi/jsi/test/testlib.cpp | 1 + android/ReactCommon/jsiexecutor/Android.mk | 4 +- .../jsiexecutor/React-jsiexecutor.podspec | 8 +- .../jsiexecutor/jsireact/JSIExecutor.cpp | 39 +- .../jsiexecutor/jsireact/JSINativeModules.cpp | 2 + android/ReactCommon/jsinspector/BUCK | 7 +- .../jsinspector/React-jsinspector.podspec | 4 +- .../src/test/java/com/facebook/powermock/BUCK | 2 + android/ReactCommon/logger/Android.mk | 23 + android/ReactCommon/logger/BUCK | 38 + .../ReactCommon/logger/React-logger.podspec | 40 + .../ReactCommon/logger/react_native_log.cpp | 52 + android/ReactCommon/logger/react_native_log.h | 33 + android/ReactCommon/react/config/Android.mk | 2 +- android/ReactCommon/react/config/BUCK | 6 - android/ReactCommon/react/debug/Android.mk | 31 + android/ReactCommon/react/debug/BUCK | 63 + android/ReactCommon/react/debug/flags.h | 19 + .../react/debug/react_native_assert.cpp | 45 + .../react/debug/react_native_assert.h | 64 + .../react/nativemodule/core/Android.mk | 6 +- .../ReactCommon/react/nativemodule/core/BUCK | 10 +- .../core/ReactCommon/LongLivedObject.cpp | 1 + .../core/ReactCommon/LongLivedObject.h | 5 +- .../core/ReactCommon/TurboModule.cpp | 2 +- .../core/ReactCommon/TurboModule.h | 4 +- .../core/ReactCommon/TurboModuleBinding.cpp | 57 +- .../core/ReactCommon/TurboModuleBinding.h | 14 +- .../core/ReactCommon/TurboModuleUtils.h | 56 +- .../android/ReactCommon/JavaTurboModule.cpp | 228 +- .../android/ReactCommon/JavaTurboModule.h | 20 +- .../core/platform/ios/RCTBlockGuard.h | 24 + .../core/platform/ios/RCTBlockGuard.mm | 28 + .../core/platform/ios/RCTTurboModule.h | 22 +- .../core/platform/ios/RCTTurboModule.mm | 145 +- .../core/platform/ios/RCTTurboModuleManager.h | 6 - .../platform/ios/RCTTurboModuleManager.mm | 214 +- .../react/nativemodule/samples/BUCK | 17 +- .../samples/platform/android/Android.mk | 4 +- .../android/NativeSampleTurboModuleSpec.java | 3 + .../platform/android/SampleTurboModule.java | 10 + .../ios/RCTNativeSampleTurboModuleSpec.h | 1 + .../ios/RCTNativeSampleTurboModuleSpec.mm | 11 + .../platform/ios/RCTSampleTurboCxxModule.mm | 12 + .../platform/ios/RCTSampleTurboModule.mm | 8 +- .../ios/SampleTurboCxxModuleLegacyImpl.cpp | 11 + .../ios/SampleTurboCxxModuleLegacyImpl.h | 1 + .../react/renderer/animations/Android.mk | 5 +- .../react/renderer/animations/BUCK | 11 +- .../LayoutAnimationCallbackWrapper.h | 34 + .../animations/LayoutAnimationDriver.cpp | 164 +- .../animations/LayoutAnimationDriver.h | 25 +- .../LayoutAnimationKeyFrameManager.cpp | 2387 +++++------ .../LayoutAnimationKeyFrameManager.h | 344 +- .../react/renderer/animations/conversions.h | 212 + .../react/renderer/animations/primitives.h | 104 + .../animations/tests/LayoutAnimationTest.cpp | 544 +++ .../react/renderer/animations/utils.cpp | 71 + .../react/renderer/animations/utils.h | 80 + .../renderer/attributedstring/Android.mk | 6 +- .../attributedstring/AttributedString.h | 1 - .../attributedstring/AttributedStringBox.cpp | 34 +- .../attributedstring/AttributedStringBox.h | 4 +- .../react/renderer/attributedstring/BUCK | 15 +- .../attributedstring/ParagraphAttributes.cpp | 10 +- .../attributedstring/ParagraphAttributes.h | 14 +- .../attributedstring/TextAttributes.cpp | 9 +- .../attributedstring/TextAttributes.h | 4 +- .../renderer/attributedstring/conversions.h | 1023 +++-- .../renderer/attributedstring/primitives.h | 35 +- .../tests/AttributedStringBoxTest.cpp | 105 + .../tests/AttributedStringTest.cpp | 37 +- .../tests/ParagraphAttributesTest.cpp | 19 +- .../tests/TextAttributesTest.cpp | 28 +- .../renderer/componentregistry/Android.mk | 5 +- .../react/renderer/componentregistry/BUCK | 13 +- .../ComponentDescriptorProvider.h | 9 +- .../ComponentDescriptorProviderRegistry.cpp | 4 +- .../ComponentDescriptorProviderRegistry.h | 1 + .../ComponentDescriptorRegistry.cpp | 90 +- .../ComponentDescriptorRegistry.h | 5 +- .../componentNameByReactViewName.cpp | 73 + .../componentNameByReactViewName.h} | 9 +- .../componentregistry/native/Android.mk | 29 + .../renderer/componentregistry/native/BUCK | 52 + .../native/NativeComponentRegistryBinding.cpp | 68 + .../native/NativeComponentRegistryBinding.h | 61 + .../renderer/components/image/Android.mk | 8 +- .../react/renderer/components/image/BUCK | 12 +- .../image/ImageComponentDescriptor.h | 3 +- .../renderer/components/image/ImageProps.cpp | 37 +- .../renderer/components/image/ImageProps.h | 6 +- .../components/image/ImageShadowNode.cpp | 10 +- .../renderer/components/image/ImageState.h | 10 +- .../renderer/components/image/conversions.h | 47 +- .../renderer/components/inputaccessory/BUCK | 11 +- .../InputAccessoryComponentDescriptor.h | 6 +- .../components/legacyviewmanagerinterop/BUCK | 10 +- ...acyViewManagerInteropComponentDescriptor.h | 2 +- ...cyViewManagerInteropComponentDescriptor.mm | 21 +- .../LegacyViewManagerInteropViewProps.cpp | 7 +- .../LegacyViewManagerInteropViewProps.h | 2 + .../RCTLegacyViewManagerInteropCoordinator.h | 2 +- .../RCTLegacyViewManagerInteropCoordinator.mm | 89 +- .../renderer/components/modal/Android.mk | 7 +- .../react/renderer/components/modal/BUCK | 10 +- .../modal/ModalHostViewComponentDescriptor.h | 5 +- .../components/modal/ModalHostViewState.h | 11 +- .../renderer/components/picker/Android.mk | 37 - .../AndroidDialogPickerEventEmitter.cpp | 23 - .../AndroidDialogPickerEventEmitter.h | 27 - .../AndroidDialogPickerProps.cpp | 28 - .../androidpicker/AndroidDialogPickerProps.h | 77 - .../AndroidDialogPickerShadowNode.cpp | 16 - .../AndroidDialogPickerShadowNode.h | 29 - .../AndroidDropdownPickerEventEmitter.cpp | 23 - .../AndroidDropdownPickerEventEmitter.h | 27 - .../AndroidDropdownPickerProps.cpp | 28 - .../AndroidDropdownPickerProps.h | 77 - .../AndroidDropdownPickerShadowNode.h | 29 - .../iospicker/PickerComponentDescriptor.h | 26 - .../picker/iospicker/PickerEventEmitter.cpp | 23 - .../picker/iospicker/PickerProps.cpp | 46 - .../components/picker/iospicker/PickerProps.h | 36 - .../picker/iospicker/PickerShadowNode.h | 33 - .../components/picker/iospicker/conversions.h | 47 - .../components/progressbar/Android.mk | 6 +- .../renderer/components/progressbar/BUCK | 11 +- .../AndroidProgressBarComponentDescriptor.h | 3 +- .../components/rncore/ComponentDescriptors.h | 11 +- .../components/rncore/EventEmitters.cpp | 83 +- .../components/rncore/EventEmitters.h | 114 +- .../renderer/components/rncore/Props.cpp | 218 + .../react/renderer/components/rncore/Props.h | 357 +- .../rncore/RCTComponentViewHelpers.h | 273 ++ .../components/rncore/ShadowNodes.cpp | 11 +- .../renderer/components/rncore/ShadowNodes.h | 89 +- .../react/renderer/components/root/Android.mk | 7 +- .../react/renderer/components/root/BUCK | 11 +- .../renderer/components/root/RootProps.cpp | 14 +- .../renderer/components/root/RootProps.h | 7 +- .../components/root/RootShadowNode.cpp | 8 +- .../renderer/components/root/RootShadowNode.h | 2 + .../root/tests/RootShadowNodeTest.cpp | 38 +- .../renderer/components/safeareaview/BUCK | 11 +- .../SafeAreaViewComponentDescriptor.h | 8 +- .../safeareaview/SafeAreaViewShadowNode.h | 7 + .../safeareaview/SafeAreaViewState.h | 4 +- .../renderer/components/scrollview/Android.mk | 8 +- .../react/renderer/components/scrollview/BUCK | 11 +- .../scrollview/RCTComponentViewHelpers.h | 38 + .../components/scrollview/ScrollViewProps.cpp | 65 +- .../components/scrollview/ScrollViewProps.h | 15 +- .../scrollview/ScrollViewShadowNode.cpp | 25 +- .../scrollview/ScrollViewShadowNode.h | 1 + .../components/scrollview/ScrollViewState.cpp | 2 +- .../components/scrollview/ScrollViewState.h | 19 +- .../components/scrollview/conversions.h | 5 + .../renderer/components/slider/Android.mk | 7 +- .../react/renderer/components/slider/BUCK | 12 +- .../slider/SliderComponentDescriptor.h | 3 +- .../components/slider/SliderShadowNode.h | 17 +- .../renderer/components/slider/SliderState.h | 8 + .../ios/SliderMeasurementsManager.cpp | 4 +- .../renderer/components/switch/Android.mk | 6 +- .../react/renderer/components/switch/BUCK | 10 +- .../AndroidSwitchComponentDescriptor.h | 3 +- .../react/renderer/components/text/Android.mk | 8 +- .../react/renderer/components/text/BUCK | 18 +- .../components/text/BaseTextProps.cpp | 32 + .../renderer/components/text/BaseTextProps.h | 6 +- .../components/text/BaseTextShadowNode.cpp | 5 +- .../text/ParagraphComponentDescriptor.h | 26 +- .../components/text/ParagraphProps.cpp | 21 +- .../renderer/components/text/ParagraphProps.h | 6 +- .../components/text/ParagraphShadowNode.cpp | 32 +- .../components/text/ParagraphShadowNode.h | 13 +- .../components/text/ParagraphState.cpp | 4 + .../renderer/components/text/ParagraphState.h | 11 +- .../renderer/components/text/RawTextProps.cpp | 5 +- .../renderer/components/text/RawTextProps.h | 6 +- .../components/text/RawTextShadowNode.h | 38 +- .../renderer/components/text/TextProps.cpp | 9 +- .../renderer/components/text/TextProps.h | 6 +- .../renderer/components/text/TextShadowNode.h | 23 + .../renderer/components/text/conversions.h | 22 + .../text/tests/ParagraphLocalDataTest.cpp | 13 +- .../renderer/components/textinput/Android.mk | 8 +- .../react/renderer/components/textinput/BUCK | 16 +- .../AndroidTextInputComponentDescriptor.h | 10 +- .../AndroidTextInputProps.cpp | 149 +- .../androidtextinput/AndroidTextInputProps.h | 18 +- .../AndroidTextInputShadowNode.cpp | 31 +- .../AndroidTextInputShadowNode.h | 10 +- .../AndroidTextInputState.cpp | 3 - .../androidtextinput/AndroidTextInputState.h | 13 +- .../components/textinput/iostextinput/BUCK | 13 +- .../TextInputComponentDescriptor.h | 5 +- .../textinput/iostextinput/TextInputProps.cpp | 68 +- .../textinput/iostextinput/TextInputProps.h | 11 +- .../iostextinput/TextInputShadowNode.cpp | 5 +- .../iostextinput/TextInputShadowNode.h | 13 +- .../textinput/iostextinput/TextInputState.h | 2 + .../textinput/iostextinput/conversions.h | 26 +- .../textinput/iostextinput/primitives.h | 6 + .../textinput/iostextinput/propsConversions.h | 65 +- .../components/unimplementedview/Android.mk | 7 +- .../components/unimplementedview/BUCK | 11 +- .../UnimplementedViewComponentDescriptor.cpp | 7 +- .../UnimplementedViewComponentDescriptor.h | 7 +- .../UnimplementedViewProps.h | 1 + .../components/view/AccessibilityPrimitives.h | 28 + .../components/view/AccessibilityProps.cpp | 25 +- .../components/view/AccessibilityProps.h | 5 +- .../react/renderer/components/view/Android.mk | 10 +- .../react/renderer/components/view/BUCK | 12 +- .../renderer/components/view/TouchEvent.h | 2 - .../components/view/TouchEventEmitter.cpp | 40 +- .../components/view/TouchEventEmitter.h | 3 +- .../components/view/ViewComponentDescriptor.h | 14 - .../components/view/ViewEventEmitter.cpp | 82 +- .../components/view/ViewEventEmitter.h | 32 +- .../renderer/components/view/ViewProps.cpp | 76 +- .../renderer/components/view/ViewProps.h | 8 +- .../components/view/ViewPropsInterpolation.h | 13 +- .../components/view/ViewShadowNode.cpp | 27 +- .../renderer/components/view/ViewShadowNode.h | 2 +- .../view/YogaLayoutableShadowNode.cpp | 264 +- .../view/YogaLayoutableShadowNode.h | 13 +- .../components/view/YogaStylableProps.cpp | 5 +- .../components/view/YogaStylableProps.h | 2 + .../view/accessibilityPropsConversions.h | 123 +- .../renderer/components/view/conversions.h | 146 +- .../renderer/components/view/primitives.h | 9 +- .../components/view/propsConversions.h | 191 +- .../components/view/tests/ViewTest.cpp | 20 +- .../react/renderer/core/Android.mk | 6 +- android/ReactCommon/react/renderer/core/BUCK | 17 +- .../react/renderer/core/BatchedEventQueue.cpp | 26 +- .../react/renderer/core/BatchedEventQueue.h | 12 +- .../react/renderer/core/ComponentDescriptor.h | 9 +- .../core/ConcreteComponentDescriptor.h | 56 +- .../react/renderer/core/ConcreteShadowNode.h | 22 +- .../react/renderer/core/ConcreteState.h | 65 +- .../react/renderer/core/ConcreteStateTeller.h | 152 - .../renderer/core/DynamicPropsUtilities.cpp | 35 + .../DynamicPropsUtilities.h} | 9 +- .../react/renderer/core/EventDispatcher.cpp | 24 +- .../react/renderer/core/EventDispatcher.h | 15 +- .../react/renderer/core/EventEmitter.cpp | 29 +- .../react/renderer/core/EventEmitter.h | 10 +- .../react/renderer/core/EventPipe.h | 2 + .../react/renderer/core/EventPriority.h | 2 +- .../react/renderer/core/EventQueue.cpp | 79 +- .../react/renderer/core/EventQueue.h | 24 +- .../renderer/core/EventQueueProcessor.cpp | 82 + .../react/renderer/core/EventQueueProcessor.h | 36 + .../react/renderer/core/EventTarget.cpp | 6 +- .../react/renderer/core/LayoutConstraints.h | 11 +- .../react/renderer/core/LayoutMetrics.cpp | 65 + .../react/renderer/core/LayoutMetrics.h | 42 +- .../react/renderer/core/LayoutPrimitives.h | 20 +- .../renderer/core/LayoutableShadowNode.cpp | 9 +- .../renderer/core/LayoutableShadowNode.h | 24 +- .../ReactCommon/react/renderer/core/Props.cpp | 12 +- .../ReactCommon/react/renderer/core/Props.h | 7 +- .../react/renderer/core/PropsParserContext.h | 29 + .../react/renderer/core/RawEvent.cpp | 6 +- .../react/renderer/core/RawEvent.h | 45 +- .../react/renderer/core/RawProps.cpp | 8 +- .../react/renderer/core/RawProps.h | 4 +- .../react/renderer/core/RawPropsKey.cpp | 29 +- .../react/renderer/core/RawPropsKeyMap.cpp | 14 +- .../react/renderer/core/RawPropsParser.cpp | 17 +- .../react/renderer/core/RawPropsParser.h | 17 +- .../react/renderer/core/RawValue.h | 16 +- .../react/renderer/core/ReactEventPriority.h | 43 + .../react/renderer/core/ReactPrimitives.h | 36 +- .../react/renderer/core/Sealable.cpp | 7 +- .../react/renderer/core/Sealable.h | 6 +- .../react/renderer/core/ShadowNode.cpp | 55 +- .../react/renderer/core/ShadowNode.h | 18 +- .../react/renderer/core/ShadowNodeFamily.cpp | 3 +- .../react/renderer/core/ShadowNodeFamily.h | 3 +- .../react/renderer/core/ShadowNodeTraits.h | 37 +- .../ReactCommon/react/renderer/core/State.h | 7 +- .../react/renderer/core/StateData.h | 3 + .../react/renderer/core/StateUpdate.h | 2 - .../renderer/core/UnbatchedEventQueue.cpp | 2 - .../react/renderer/core/conversions.h | 11 + .../react/renderer/core/propsConversions.h | 28 +- .../core/tests/ComponentDescriptorTest.cpp | 20 +- .../core/tests/ConcreteShadowNodeTest.cpp | 2 +- .../core/tests/DynamicPropsUtilitiesTest.cpp | 66 + .../core/tests/EventQueueProcessorTest.cpp | 134 + .../core/tests/LayoutableShadowNodeTest.cpp | 33 +- .../renderer/core/tests/RawPropsTest.cpp | 129 +- .../react/renderer/core/tests/TestComponent.h | 18 +- .../renderer/core/tests/traitCastTest.cpp | 31 + .../react/renderer/debug/Android.mk | 2 +- android/ReactCommon/react/renderer/debug/BUCK | 14 +- .../renderer/debug/DebugStringConvertible.h | 36 +- .../react/renderer/debug/SystraceSection.h | 4 +- .../ReactCommon/react/renderer/debug/flags.h | 50 + .../ReactCommon/react/renderer/element/BUCK | 10 +- .../renderer/element/ComponentBuilder.cpp | 7 +- .../react/renderer/graphics/Android.mk | 10 +- .../ReactCommon/react/renderer/graphics/BUCK | 24 +- .../renderer/graphics/React-graphics.podspec | 10 +- .../react/renderer/graphics/Rect.h | 7 +- .../renderer/graphics/RectangleCorners.h | 4 +- .../react/renderer/graphics/RectangleEdges.h | 29 +- .../react/renderer/graphics/Transform.cpp | 9 +- .../react/renderer/graphics/Transform.h | 2 +- .../react/renderer/graphics/conversions.h | 146 +- .../renderer/graphics/PlatformColorParser.h | 67 + .../cxx/react/renderer/graphics/Color.cpp | 29 +- .../cxx/react/renderer/graphics/Color.h | 2 + .../renderer/graphics/PlatformColorParser.h | 29 + .../renderer/graphics/platform/ios/Color.cpp | 29 +- .../renderer/graphics/platform/ios/Color.h | 45 +- .../platform/ios/PlatformColorParser.h | 34 + .../platform/ios/RCTPlatformColorUtils.h | 12 + .../platform/ios/RCTPlatformColorUtils.mm | 206 + .../react/renderer/graphics/rounding.h | 15 +- .../renderer/graphics/tests/TransformTest.cpp | 1 + .../react/renderer/imagemanager/Android.mk | 5 +- .../react/renderer/imagemanager/BUCK | 13 +- .../renderer/imagemanager/ImageRequest.h | 4 +- .../ImageResponseObserverCoordinator.cpp | 9 +- .../renderer/imagemanager/ImageTelemetry.cpp | 6 - .../renderer/imagemanager/ImageTelemetry.h | 15 +- .../platform/ios/ImageRequest.cpp | 7 +- .../platform/ios/RCTImageManager.mm | 1 - .../ios/RCTImagePrimitivesConversions.h | 14 +- .../platform/ios/RCTSyncImageManager.mm | 2 +- .../react/renderer/leakchecker/Android.mk | 29 + .../picker/iospicker => leakchecker}/BUCK | 24 +- .../renderer/leakchecker/LeakChecker.cpp | 60 + .../react/renderer/leakchecker/LeakChecker.h | 39 + .../leakchecker/WeakFamilyRegistry.cpp | 37 + .../renderer/leakchecker/WeakFamilyRegistry.h | 39 + .../react/renderer/mapbuffer/Android.mk | 15 +- .../ReactCommon/react/renderer/mapbuffer/BUCK | 26 +- .../react/renderer/mapbuffer/MapBuffer.cpp | 135 +- .../react/renderer/mapbuffer/MapBuffer.h | 44 +- .../renderer/mapbuffer/MapBufferBuilder.cpp | 226 + .../renderer/mapbuffer/MapBufferBuilder.h | 94 + .../react/renderer/mapbuffer/primitives.h | 91 + .../mapbuffer/tests/MapBufferTest.cpp | 146 +- .../react/renderer/mounting/Android.mk | 7 +- .../ReactCommon/react/renderer/mounting/BUCK | 13 +- .../renderer/mounting/Differentiator.cpp | 1807 ++++---- .../react/renderer/mounting/Differentiator.h | 50 +- .../renderer/mounting/MountingCoordinator.cpp | 32 +- .../renderer/mounting/MountingCoordinator.h | 18 +- .../mounting/MountingOverrideDelegate.h | 2 +- .../renderer/mounting/MountingTransaction.h | 4 +- .../mounting/MountingTransactionMetadata.h | 2 +- .../react/renderer/mounting/ShadowTree.cpp | 97 +- .../react/renderer/mounting/ShadowTree.h | 53 +- .../renderer/mounting/ShadowTreeDelegate.h | 11 + .../renderer/mounting/ShadowTreeRegistry.cpp | 4 +- .../renderer/mounting/ShadowTreeRevision.h | 2 +- .../react/renderer/mounting/ShadowView.cpp | 20 +- .../react/renderer/mounting/ShadowView.h | 48 +- .../renderer/mounting/ShadowViewMutation.cpp | 36 +- .../renderer/mounting/ShadowViewMutation.h | 23 +- .../react/renderer/mounting/StubView.cpp | 42 +- .../react/renderer/mounting/StubView.h | 6 + .../react/renderer/mounting/StubViewTree.cpp | 310 +- .../react/renderer/mounting/StubViewTree.h | 18 +- .../renderer/mounting/TelemetryController.cpp | 2 +- .../renderer/mounting/TelemetryController.h | 2 +- .../mounting/TransactionTelemetry.cpp | 142 - .../react/renderer/mounting/stubs.cpp | 60 +- .../react/renderer/mounting/stubs.h | 14 +- .../renderer/mounting/tests/MountingTest.cpp | 195 +- .../tests/ShadowTreeLifeCycleTest.cpp | 282 +- .../mounting/tests/StackingContextTest.cpp | 787 ++++ .../tests/StateReconciliationTest.cpp | 57 +- .../tests/TransactionTelemetryTest.cpp | 125 - .../renderer/runtimescheduler/Android.mk | 28 + .../react/renderer/runtimescheduler/BUCK | 71 + .../renderer/runtimescheduler/ErrorUtils.h | 36 + .../runtimescheduler/RuntimeScheduler.cpp | 124 + .../runtimescheduler/RuntimeScheduler.h | 105 + .../RuntimeSchedulerBinding.cpp | 171 + .../RuntimeSchedulerBinding.h | 44 + .../RuntimeSchedulerCallInvoker.cpp | 32 + .../RuntimeSchedulerCallInvoker.h | 36 + .../RuntimeSchedulerClock.h} | 13 +- .../runtimescheduler/SchedulerPriority.h | 65 + .../react/renderer/runtimescheduler/Task.cpp | 37 + .../react/renderer/runtimescheduler/Task.h | 48 + .../renderer/runtimescheduler/primitives.h | 41 + .../tests/RuntimeSchedulerTest.cpp | 497 +++ .../tests/SchedulerPriorityTest.cpp | 46 + .../runtimescheduler/tests/StubClock.h | 41 + .../runtimescheduler/tests/StubErrorUtils.h | 72 + .../runtimescheduler/tests/StubQueue.h | 38 + .../react/renderer/scheduler/Android.mk | 6 +- .../scheduler/AsynchronousEventBeat.cpp | 29 +- .../scheduler/AsynchronousEventBeat.h | 8 +- .../ReactCommon/react/renderer/scheduler/BUCK | 12 +- .../InspectorData.h} | 13 +- .../react/renderer/scheduler/Scheduler.cpp | 253 +- .../react/renderer/scheduler/Scheduler.h | 76 +- .../renderer/scheduler/SchedulerDelegate.h | 26 +- .../renderer/scheduler/SchedulerToolbox.h | 10 + .../renderer/scheduler/SurfaceHandler.cpp | 317 ++ .../react/renderer/scheduler/SurfaceHandler.h | 209 + .../renderer/scheduler/SurfaceManager.cpp | 104 + .../react/renderer/scheduler/SurfaceManager.h | 67 + .../scheduler/SynchronousEventBeat.cpp | 19 +- .../renderer/scheduler/SynchronousEventBeat.h | 5 +- .../react/renderer/telemetry/Android.mk | 31 + .../ReactCommon/react/renderer/telemetry/BUCK | 78 + .../SurfaceTelemetry.cpp | 5 + .../SurfaceTelemetry.h | 4 +- .../telemetry/TransactionTelemetry.cpp | 163 + .../TransactionTelemetry.h | 11 + .../tests/TransactionTelemetryTest.cpp | 176 + .../renderer/templateprocessor/Android.mk | 2 +- .../react/renderer/templateprocessor/BUCK | 10 +- .../templateprocessor/UITemplateProcessor.cpp | 10 +- .../tests/UITemplateProcessorTest.cpp | 10 +- .../renderer/textlayoutmanager/Android.mk | 7 +- .../react/renderer/textlayoutmanager/BUCK | 12 +- .../textlayoutmanager/TextMeasureCache.cpp | 21 +- .../textlayoutmanager/TextLayoutManager.cpp | 228 +- .../textlayoutmanager/TextLayoutManager.h | 18 +- .../platform/cxx/TextLayoutManager.cpp | 18 +- .../platform/cxx/TextLayoutManager.h | 17 +- .../platform/ios/RCTAttributedTextUtils.h | 2 + .../platform/ios/RCTAttributedTextUtils.mm | 46 +- .../platform/ios/RCTTextLayoutManager.mm | 25 +- .../platform/ios/TextLayoutManager.mm | 28 +- .../{components/picker => timeline}/BUCK | 46 +- .../react/renderer/timeline/Timeline.cpp | 120 + .../react/renderer/timeline/Timeline.h | 63 + .../renderer/timeline/TimelineController.cpp | 77 + .../renderer/timeline/TimelineController.h | 80 + .../react/renderer/timeline/TimelineFrame.cpp | 25 + .../react/renderer/timeline/TimelineFrame.h | 46 + .../renderer/timeline/TimelineHandler.cpp | 97 + .../react/renderer/timeline/TimelineHandler.h | 80 + .../renderer/timeline/TimelineSnapshot.cpp | 30 + .../renderer/timeline/TimelineSnapshot.h | 36 + .../react/renderer/uimanager/Android.mk | 6 +- .../ReactCommon/react/renderer/uimanager/BUCK | 12 +- .../uimanager/LayoutAnimationStatusDelegate.h | 6 +- .../react/renderer/uimanager/UIManager.cpp | 253 +- .../react/renderer/uimanager/UIManager.h | 92 +- .../uimanager/UIManagerAnimationDelegate.h | 8 +- .../renderer/uimanager/UIManagerBinding.cpp | 433 +- .../renderer/uimanager/UIManagerBinding.h | 41 +- .../renderer/uimanager/UIManagerCommitHook.h | 43 + .../renderer/uimanager/UIManagerDelegate.h | 36 +- .../react/renderer/uimanager/primitives.h | 66 +- android/ReactCommon/react/test_utils/BUCK | 51 + .../mounting/tests => test_utils}/Entropy.h | 4 + .../ReactCommon/react/test_utils/MockClock.h | 29 + .../MockSurfaceHandler.h} | 15 +- .../shadowTreeGeneration.h | 71 +- android/ReactCommon/react/utils/Android.mk | 11 +- android/ReactCommon/react/utils/BUCK | 13 +- .../utils/CalledOnceMovableOnlyFunction.h | 10 +- .../react/utils/ContextContainer.h | 28 +- .../ReactCommon/react/utils/LayoutManager.h | 150 + .../react/utils/ManagedObjectWrapper.h | 4 +- .../react/utils/RunLoopObserver.cpp | 9 +- .../ReactCommon/reactperflogger/Android.mk | 2 +- android/ReactCommon/reactperflogger/BUCK | 4 - .../reactperflogger/React-perflogger.podspec | 6 +- .../ReactCommon/runtimeexecutor/Android.mk | 2 +- android/ReactCommon/runtimeexecutor/BUCK | 8 +- .../React-runtimeexecutor.podspec | 6 +- .../ReactCommon/RuntimeExecutor.h | 16 +- android/ReactCommon/yoga/BUCK | 6 +- android/ReactCommon/yoga/yoga.podspec | 4 +- android/ReactCommon/yoga/yoga/BitUtils.h | 5 +- android/ReactCommon/yoga/yoga/CompactValue.h | 4 +- android/ReactCommon/yoga/yoga/Utils.cpp | 2 +- android/ReactCommon/yoga/yoga/YGNode.cpp | 221 +- android/ReactCommon/yoga/yoga/YGNode.h | 11 + android/ReactCommon/yoga/yoga/YGNodePrint.cpp | 11 +- android/ReactCommon/yoga/yoga/Yoga-internal.h | 9 +- android/ReactCommon/yoga/yoga/Yoga.cpp | 277 +- android/ReactCommon/yoga/yoga/Yoga.h | 2 +- android/build.gradle | 17 +- android/expoview/build.gradle | 40 +- .../exponent/network/ExponentHttpClient.kt | 14 +- .../exponent/network/OkHttpV1ExpoResponse.kt | 8 +- .../ExponentNotificationIntentService.kt | 6 +- .../notifications/NotificationHelper.kt | 6 +- .../main/java/host/exp/expoview/Exponent.kt | 4 +- .../ReanimatedUIImplementation.java | 2 +- .../modules/internal/DevMenuModule.kt | 8 +- android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 4 +- android/tools/build.gradle | 8 +- .../tools/ReactAndroidCodeTransformer.java | 35 +- .../modules/filesystem/CountingRequestBody.kt | 8 +- .../modules/filesystem/FileSystemModule.kt | 53 +- apps/bare-expo/android/gradle.properties | 2 +- apps/bare-expo/ios/Podfile.lock | 6 +- ios/ExpoKit.podspec | 2 +- ios/Exponent.xcodeproj/project.pbxproj | 8 +- .../Versioned/Core/EXVersionManager.mm | 15 +- .../DevSupport/EXDisabledDevLoadingView.h | 3 +- .../DevSupport/EXDisabledDevLoadingView.m | 17 + .../Core/UniversalModules/EXScopedBranch.m | 8 +- ios/Podfile | 3 + ios/Podfile.lock | 829 ++-- .../ABI42_0_0RNReanimated.podspec.json | 4 +- .../ABI43_0_0RNReanimated.podspec.json | 4 +- .../RNReanimated.podspec.json | 4 +- .../Expo/ExpoKit/ABI42_0_0ExpoKit.podspec | 8 +- .../ReactNative/ABI42_0_0React-Core.podspec | 4 +- .../Blob/ABI42_0_0React-RCTBlob.podspec | 4 +- .../ABI42_0_0FBReactNativeSpec.podspec | 4 +- .../Image/ABI42_0_0React-RCTImage.podspec | 4 +- .../ABI42_0_0React-RCTLinking.podspec | 4 +- .../ABI42_0_0React-RCTAnimation.podspec | 4 +- .../Network/ABI42_0_0React-RCTNetwork.podspec | 4 +- ...ABI42_0_0React-RCTPushNotification.podspec | 4 +- .../ABI42_0_0React-RCTSettings.podspec | 4 +- .../TypeSafety/ABI42_0_0RCTTypeSafety.podspec | 4 +- .../ABI42_0_0React-RCTVibration.podspec | 4 +- .../React/ABI42_0_0React-RCTFabric.podspec | 6 +- .../ABI42_0_0React-CoreModules.podspec | 4 +- .../ReactNative/React/third-party.xcconfig | 2 +- .../ReactCommon/ABI42_0_0React-Fabric.podspec | 38 +- .../ReactCommon/ABI42_0_0ReactCommon.podspec | 4 +- .../ABI42_0_0React-callinvoker.podspec | 2 +- .../cxxreact/ABI42_0_0React-cxxreact.podspec | 6 +- .../graphics/ABI42_0_0React-graphics.podspec | 4 +- .../jsi/ABI42_0_0React-jsi.podspec | 6 +- .../ABI42_0_0React-jsiexecutor.podspec | 4 +- .../Expo/ExpoKit/ABI43_0_0ExpoKit.podspec | 6 +- .../ReactNative/ABI43_0_0React-Core.podspec | 4 +- .../Blob/ABI43_0_0React-RCTBlob.podspec | 2 +- .../Image/ABI43_0_0React-RCTImage.podspec | 2 +- .../ABI43_0_0React-RCTLinking.podspec | 2 +- .../ABI43_0_0React-RCTAnimation.podspec | 2 +- .../Network/ABI43_0_0React-RCTNetwork.podspec | 2 +- ...ABI43_0_0React-RCTPushNotification.podspec | 2 +- .../ABI43_0_0React-RCTSettings.podspec | 2 +- .../TypeSafety/ABI43_0_0RCTTypeSafety.podspec | 2 +- .../ABI43_0_0React-RCTVibration.podspec | 2 +- .../React/ABI43_0_0React-RCTFabric.podspec | 6 +- .../ABI43_0_0React-CoreModules.podspec | 2 +- .../ABI43_0_0FBReactNativeSpec.podspec | 2 +- .../ReactNative/React/third-party.xcconfig | 2 +- .../ReactCommon/ABI43_0_0React-Fabric.podspec | 4 +- .../ReactCommon/ABI43_0_0ReactCommon.podspec | 4 +- .../ABI43_0_0React-callinvoker.podspec | 2 +- .../cxxreact/ABI43_0_0React-cxxreact.podspec | 6 +- .../jsi/ABI43_0_0React-jsi.podspec | 6 +- .../ABI43_0_0React-jsiexecutor.podspec | 4 +- .../graphics/ABI43_0_0React-graphics.podspec | 4 +- .../ABI43_0_0React-perflogger.podspec | 2 +- .../ABI43_0_0React-runtimeexecutor.podspec | 2 +- .../Expo/ExpoKit/ABI44_0_0ExpoKit.podspec | 6 +- .../ReactNative/ABI44_0_0React-Core.podspec | 4 +- .../Blob/ABI44_0_0React-RCTBlob.podspec | 2 +- .../Image/ABI44_0_0React-RCTImage.podspec | 2 +- .../ABI44_0_0React-RCTLinking.podspec | 2 +- .../ABI44_0_0React-RCTAnimation.podspec | 2 +- .../Network/ABI44_0_0React-RCTNetwork.podspec | 2 +- ...ABI44_0_0React-RCTPushNotification.podspec | 2 +- .../ABI44_0_0React-RCTSettings.podspec | 2 +- .../TypeSafety/ABI44_0_0RCTTypeSafety.podspec | 2 +- .../ABI44_0_0React-RCTVibration.podspec | 2 +- .../React/ABI44_0_0React-RCTFabric.podspec | 6 +- .../ABI44_0_0React-CoreModules.podspec | 2 +- .../ABI44_0_0FBReactNativeSpec.podspec | 2 +- .../ReactNative/React/third-party.xcconfig | 2 +- .../ReactCommon/ABI44_0_0React-Fabric.podspec | 4 +- .../ReactCommon/ABI44_0_0ReactCommon.podspec | 4 +- .../ABI44_0_0React-callinvoker.podspec | 2 +- .../cxxreact/ABI44_0_0React-cxxreact.podspec | 6 +- .../jsi/ABI44_0_0React-jsi.podspec | 6 +- .../ABI44_0_0React-jsiexecutor.podspec | 4 +- .../graphics/ABI44_0_0React-graphics.podspec | 4 +- .../ABI44_0_0React-perflogger.podspec | 2 +- .../ABI44_0_0React-runtimeexecutor.podspec | 2 +- packages/expo/android/build.gradle | 7 - .../expo/android/src/test/AndroidManifest.xml | 18 + react-native-lab/react-native | 2 +- template-files/ios/ExpoKit-Podfile | 16 +- template-files/ios/ExpoKit-Podfile-versioned | 3 + template-files/ios/ExpoKit.podspec | 2 +- tools/src/commands/AndroidBuildPackages.ts | 6 +- tools/src/commands/UpdateReactNative.ts | 16 +- tools/src/vendoring/config/expoGoConfig.ts | 2 +- tools/src/vendoring/legacy.ts | 2 +- 1166 files changed, 36050 insertions(+), 19763 deletions(-) delete mode 100644 android/ReactAndroid/release.gradle delete mode 100644 android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/DatePickerDialogTestCase.java delete mode 100644 android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ReactPickerTestCase.java create mode 100644 android/ReactAndroid/src/androidTest/js/BUCK delete mode 100644 android/ReactAndroid/src/androidTest/js/DatePickerDialogTestModule.js delete mode 100644 android/ReactAndroid/src/androidTest/js/PickerAndroidTestModule.js delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/ReactFragmentActivity.java rename android/ReactAndroid/src/main/java/com/facebook/react/{turbomodule/core => }/ReactPackageTurboModuleManagerDelegate.java (82%) create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactCxxErrorHandler.java rename android/ReactAndroid/src/main/java/com/facebook/react/bridge/{ReactSoftException.java => ReactSoftExceptionLogger.java} (83%) create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/bridge/RuntimeScheduler.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/BUCK create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/Android.mk create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/BUCK create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/OnLoad.cpp create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.cpp create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.h create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.java rename android/ReactAndroid/src/main/java/com/facebook/react/devsupport/{ReactInstanceManagerDevHelper.java => ReactInstanceDevHelper.java} (88%) rename android/ReactAndroid/src/main/java/com/facebook/react/{modules/datepicker/DatePickerMode.java => devsupport/interfaces/BundleLoadCallback.java} (58%) create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/ErrorType.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.cpp create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.h create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/InsertMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/RemoveDeleteMultiMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateEventEmitterMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLayoutMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePaddingMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePropsMountItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateStateMountItem.java rename android/ReactAndroid/src/main/java/com/facebook/react/modules/core/{JavaScriptTimerManager.java => JavaScriptTimerExecutor.java} (96%) delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogFragment.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogModule.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DismissableDatePickerDialog.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolver.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolverManager.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingProhibitedView.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactOverflowView.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerResolver.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventCategoryDef.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java rename android/ReactAndroid/src/main/java/com/facebook/react/{viewmanagers => uimanager/interfaces}/BUCK (56%) rename android/ReactAndroid/src/main/java/com/facebook/react/uimanager/{ => interfaces}/BaseViewManagerDelegate.java (100%) rename android/ReactAndroid/src/main/java/com/facebook/react/uimanager/{ => interfaces}/BaseViewManagerInterface.java (100%) rename android/ReactAndroid/src/main/java/com/facebook/react/uimanager/{ => interfaces}/FloatUtil.java (100%) rename android/ReactAndroid/src/main/java/com/facebook/react/uimanager/{ => interfaces}/Spacing.java (100%) rename android/ReactAndroid/src/main/java/com/facebook/react/uimanager/{ => interfaces}/ViewManagerDelegate.java (100%) rename android/ReactAndroid/src/main/java/com/facebook/react/uimanager/{ => interfaces}/ViewProps.java (96%) create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/Android.mk create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/BUCK create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.cpp create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.h create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/OnLoad.cpp delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerDelegate.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerInterface.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/Android.mk delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.cpp delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDialogPickerManager.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDropdownPickerManager.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerAdapter.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerItem.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java delete mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/picker/events/PickerItemSelectEvent.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/text/TypefaceStyle.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/react/views/view/CanvasUtil.java create mode 100644 android/ReactAndroid/src/main/java/com/facebook/yoga/YogaProps.java create mode 100644 android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.cpp rename android/{ReactCommon/react/renderer/components/picker/iospicker/primitives.h => ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.h} (51%) create mode 100644 android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.cpp create mode 100644 android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.h rename android/{ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerComponentDescriptor.h => ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.cpp} (51%) create mode 100644 android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.h create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/jump_arm_aapcs_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/make_arm_aapcs_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/ontop_arm_aapcs_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/jump_arm64_aapcs_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/make_arm64_aapcs_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/ontop_arm64_aapcs_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/jump_i386_sysv_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/make_i386_sysv_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/ontop_i386_sysv_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/jump_x86_64_sysv_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/make_x86_64_sysv_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/ontop_x86_64_sysv_elf_gas.S create mode 100644 android/ReactAndroid/src/main/jni/third-party/fmt/Android.mk create mode 100644 android/ReactAndroid/src/main/jni/third-party/libevent/Android.mk create mode 100644 android/ReactAndroid/src/main/jni/third-party/libevent/evconfig-private.h create mode 100644 android/ReactAndroid/src/main/jni/third-party/libevent/event-config.h delete mode 100644 android/ReactAndroid/src/main/third-party/android/support/v4/BUCK delete mode 100644 android/ReactAndroid/src/main/third-party/android/support/v7/appcompat/BUCK delete mode 100644 android/ReactAndroid/src/main/third-party/android/support/v7/appcompat/aar-unpacker.py rename android/ReactAndroid/src/main/third-party/java/robolectric/{4.4 => }/BUCK (99%) create mode 100644 android/ReactAndroid/src/main/third-party/kotlin/BUCK create mode 100644 android/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.java delete mode 100644 android/ReactCommon/cxxreact/re_worker_requirements create mode 100644 android/ReactCommon/hermes/React-hermes.podspec create mode 100644 android/ReactCommon/logger/Android.mk create mode 100644 android/ReactCommon/logger/BUCK create mode 100644 android/ReactCommon/logger/React-logger.podspec create mode 100644 android/ReactCommon/logger/react_native_log.cpp create mode 100644 android/ReactCommon/logger/react_native_log.h create mode 100644 android/ReactCommon/react/debug/Android.mk create mode 100644 android/ReactCommon/react/debug/BUCK create mode 100644 android/ReactCommon/react/debug/flags.h create mode 100644 android/ReactCommon/react/debug/react_native_assert.cpp create mode 100644 android/ReactCommon/react/debug/react_native_assert.h create mode 100644 android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.h create mode 100644 android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.mm create mode 100644 android/ReactCommon/react/renderer/animations/LayoutAnimationCallbackWrapper.h create mode 100644 android/ReactCommon/react/renderer/animations/conversions.h create mode 100644 android/ReactCommon/react/renderer/animations/primitives.h create mode 100644 android/ReactCommon/react/renderer/animations/tests/LayoutAnimationTest.cpp create mode 100644 android/ReactCommon/react/renderer/animations/utils.cpp create mode 100644 android/ReactCommon/react/renderer/animations/utils.h create mode 100644 android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringBoxTest.cpp create mode 100644 android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp rename android/ReactCommon/react/renderer/{components/picker/iospicker/PickerShadowNode.cpp => componentregistry/componentNameByReactViewName.h} (62%) create mode 100644 android/ReactCommon/react/renderer/componentregistry/native/Android.mk create mode 100644 android/ReactCommon/react/renderer/componentregistry/native/BUCK create mode 100644 android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.cpp create mode 100644 android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/Android.mk delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp delete mode 100644 android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h delete mode 100644 android/ReactCommon/react/renderer/components/picker/iospicker/conversions.h rename android/{ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni => ReactCommon}/react/renderer/components/rncore/ComponentDescriptors.h (91%) rename android/{ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni => ReactCommon}/react/renderer/components/rncore/EventEmitters.cpp (99%) rename android/{ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni => ReactCommon}/react/renderer/components/rncore/EventEmitters.h (96%) create mode 100644 android/ReactCommon/react/renderer/components/rncore/Props.cpp rename android/{ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni => ReactCommon}/react/renderer/components/rncore/Props.h (74%) create mode 100644 android/ReactCommon/react/renderer/components/rncore/RCTComponentViewHelpers.h rename android/{ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni => ReactCommon}/react/renderer/components/rncore/ShadowNodes.cpp (91%) rename android/{ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni => ReactCommon}/react/renderer/components/rncore/ShadowNodes.h (90%) delete mode 100644 android/ReactCommon/react/renderer/core/ConcreteStateTeller.h create mode 100644 android/ReactCommon/react/renderer/core/DynamicPropsUtilities.cpp rename android/ReactCommon/react/renderer/{components/picker/iospicker/PickerState.h => core/DynamicPropsUtilities.h} (68%) create mode 100644 android/ReactCommon/react/renderer/core/EventQueueProcessor.cpp create mode 100644 android/ReactCommon/react/renderer/core/EventQueueProcessor.h create mode 100644 android/ReactCommon/react/renderer/core/LayoutMetrics.cpp create mode 100644 android/ReactCommon/react/renderer/core/PropsParserContext.h create mode 100644 android/ReactCommon/react/renderer/core/ReactEventPriority.h create mode 100644 android/ReactCommon/react/renderer/core/tests/DynamicPropsUtilitiesTest.cpp create mode 100644 android/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp create mode 100644 android/ReactCommon/react/renderer/debug/flags.h create mode 100644 android/ReactCommon/react/renderer/graphics/platform/android/react/renderer/graphics/PlatformColorParser.h create mode 100644 android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/PlatformColorParser.h create mode 100644 android/ReactCommon/react/renderer/graphics/platform/ios/PlatformColorParser.h create mode 100644 android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.h create mode 100644 android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.mm create mode 100644 android/ReactCommon/react/renderer/leakchecker/Android.mk rename android/ReactCommon/react/renderer/{components/picker/iospicker => leakchecker}/BUCK (58%) create mode 100644 android/ReactCommon/react/renderer/leakchecker/LeakChecker.cpp create mode 100644 android/ReactCommon/react/renderer/leakchecker/LeakChecker.h create mode 100644 android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.cpp create mode 100644 android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.h create mode 100644 android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp create mode 100644 android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h create mode 100644 android/ReactCommon/react/renderer/mapbuffer/primitives.h delete mode 100644 android/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp create mode 100644 android/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp delete mode 100644 android/ReactCommon/react/renderer/mounting/tests/TransactionTelemetryTest.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/Android.mk create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/BUCK create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h rename android/ReactCommon/react/renderer/{components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerComponentDescriptor.h => runtimescheduler/RuntimeSchedulerClock.h} (50%) create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/Task.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/Task.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/primitives.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/tests/SchedulerPriorityTest.cpp create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/tests/StubClock.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/tests/StubErrorUtils.h create mode 100644 android/ReactCommon/react/renderer/runtimescheduler/tests/StubQueue.h rename android/ReactCommon/react/renderer/{components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.cpp => scheduler/InspectorData.h} (51%) create mode 100644 android/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp create mode 100644 android/ReactCommon/react/renderer/scheduler/SurfaceHandler.h create mode 100644 android/ReactCommon/react/renderer/scheduler/SurfaceManager.cpp create mode 100644 android/ReactCommon/react/renderer/scheduler/SurfaceManager.h create mode 100644 android/ReactCommon/react/renderer/telemetry/Android.mk create mode 100644 android/ReactCommon/react/renderer/telemetry/BUCK rename android/ReactCommon/react/renderer/{mounting => telemetry}/SurfaceTelemetry.cpp (93%) rename android/ReactCommon/react/renderer/{mounting => telemetry}/SurfaceTelemetry.h (91%) create mode 100644 android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.cpp rename android/ReactCommon/react/renderer/{mounting => telemetry}/TransactionTelemetry.h (85%) create mode 100644 android/ReactCommon/react/renderer/telemetry/tests/TransactionTelemetryTest.cpp rename android/ReactCommon/react/renderer/{components/picker => timeline}/BUCK (56%) create mode 100644 android/ReactCommon/react/renderer/timeline/Timeline.cpp create mode 100644 android/ReactCommon/react/renderer/timeline/Timeline.h create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineController.cpp create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineController.h create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineFrame.cpp create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineFrame.h create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineHandler.cpp create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineHandler.h create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineSnapshot.cpp create mode 100644 android/ReactCommon/react/renderer/timeline/TimelineSnapshot.h create mode 100644 android/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h create mode 100644 android/ReactCommon/react/test_utils/BUCK rename android/ReactCommon/react/{renderer/mounting/tests => test_utils}/Entropy.h (96%) create mode 100644 android/ReactCommon/react/test_utils/MockClock.h rename android/ReactCommon/react/{renderer/components/picker/iospicker/PickerEventEmitter.h => test_utils/MockSurfaceHandler.h} (51%) rename android/ReactCommon/react/{renderer/mounting/tests => test_utils}/shadowTreeGeneration.h (74%) create mode 100644 android/ReactCommon/react/utils/LayoutManager.h create mode 100644 packages/expo/android/src/test/AndroidManifest.xml diff --git a/.github/workflows/android-unit-tests.yml b/.github/workflows/android-unit-tests.yml index e7599fd082c93..4a72ade262d59 100644 --- a/.github/workflows/android-unit-tests.yml +++ b/.github/workflows/android-unit-tests.yml @@ -57,27 +57,27 @@ jobs: name: Restore NDK cache id: cache-android-ndk with: - path: /usr/local/lib/android/sdk/ndk/19.2.5345600/ - key: ${{ runner.os }}-ndk-19.2.5345600 + path: /usr/local/lib/android/sdk/ndk/21.4.7075529/ + key: ${{ runner.os }}-ndk-21.4.7075529 restore-keys: | ${{ runner.os }}-ndk- - name: Install NDK if: steps.cache-android-ndk.outputs.cache-hit != 'true' run: | - sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;19.2.5345600" + sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;21.4.7075529" - run: echo "$(pwd)/bin" >> $GITHUB_PATH - name: Patch react-native to support single abi run: sed -i 's/^APP_ABI := .*$/APP_ABI := $(if $(NDK_ABI_FILTERS),$(NDK_ABI_FILTERS),$(armeabi-v7a x86 arm64-v8a x86_64))/g' ReactAndroid/src/main/jni/Application.mk working-directory: react-native-lab/react-native - name: Run Spotless lint check env: - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ working-directory: android run: ./gradlew spotlessCheck || { echo '::error Spotless lint failed. Run `./gradlew spotlessApply` to automatically fix formatting.' && exit 1; } - name: Run native Android unit tests timeout-minutes: 30 env: - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ run: expotools native-unit-tests --platform android - name: Save test results if: always() diff --git a/.github/workflows/client-android.yml b/.github/workflows/client-android.yml index 8d9a9f303ff5f..02f23b48c1d20 100644 --- a/.github/workflows/client-android.yml +++ b/.github/workflows/client-android.yml @@ -91,14 +91,14 @@ jobs: uses: actions/cache@v2 id: cache-android-ndk with: - path: /usr/local/lib/android/sdk/ndk/19.2.5345600/ - key: ${{ runner.os }}-ndk-19.2.5345600 + path: /usr/local/lib/android/sdk/ndk/21.4.7075529/ + key: ${{ runner.os }}-ndk-21.4.7075529 restore-keys: | ${{ runner.os }}-ndk- - name: 🛠 Install NDK if: steps.cache-android-ndk.outputs.cache-hit != 'true' run: | - sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;19.2.5345600" + sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;21.4.7075529" - name: 🔎 Check which flavor to build id: flavor uses: dorny/paths-filter@v2 @@ -118,7 +118,7 @@ jobs: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ANDROID_KEY_ALIAS: ExponentKey ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ IS_APP_BUNDLE: ${{ github.event.inputs.releaseGooglePlay == 'release-google-play' }} IS_RELEASE_BUILD: ${{ github.event.inputs.releaseAPK == 'release-apk' || github.event.inputs.releaseGooglePlay == 'release-google-play' }} IS_VERSIONED_FLAVOR: ${{ github.event_name == 'schedule' || steps.flavor.outputs.versioned == 'true' }} diff --git a/.github/workflows/development-client.yml b/.github/workflows/development-client.yml index 41ca3508b60ee..5015eb2e16308 100644 --- a/.github/workflows/development-client.yml +++ b/.github/workflows/development-client.yml @@ -45,14 +45,14 @@ jobs: - uses: actions/cache@v2 id: cache-android-ndk with: - path: /usr/local/lib/android/sdk/ndk/19.2.5345600/ - key: ${{ runner.os }}-ndk-19.2.5345600 + path: /usr/local/lib/android/sdk/ndk/21.4.7075529/ + key: ${{ runner.os }}-ndk-21.4.7075529 restore-keys: | ${{ runner.os }}-ndk- - name: Install NDK if: steps.cache-android-ndk.outputs.cache-hit != 'true' run: | - sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;19.2.5345600" + sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;21.4.7075529" - run: yarn global add expo-cli - run: echo "$(yarn global bin)" >> $GITHUB_PATH - name: Init new expo app @@ -91,7 +91,7 @@ jobs: run: sed -i -e 's/com\.android\.tools\.build:gradle:3\..\../com\.android\.tools\.build:gradle:3\.5\.4/' ./android/build.gradle - name: Build debug env: - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ working-directory: ../development-client-android-test/android run: ./gradlew assembleDebug diff --git a/.github/workflows/dogfooding-clients.yml b/.github/workflows/dogfooding-clients.yml index 268e644bb1e4e..204d236d0cfab 100644 --- a/.github/workflows/dogfooding-clients.yml +++ b/.github/workflows/dogfooding-clients.yml @@ -57,14 +57,14 @@ jobs: uses: actions/cache@v2 id: cache-android-ndk with: - path: /usr/local/lib/android/sdk/ndk/19.2.5345600/ - key: ${{ runner.os }}-ndk-19.2.5345600 + path: /usr/local/lib/android/sdk/ndk/21.4.7075529/ + key: ${{ runner.os }}-ndk-21.4.7075529 restore-keys: | ${{ runner.os }}-ndk- - name: 🛠 Install NDK if: steps.cache-android-ndk.outputs.cache-hit != 'true' run: | - sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;19.2.5345600" + sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;21.4.7075529" - name: 🦴 Publish dogfood home and generate bundled manifest run: bin/expotools publish-dogfood-home env: @@ -75,7 +75,7 @@ jobs: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ANDROID_KEY_ALIAS: ExponentKey ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ USE_DOGFOODING_PUBLISHED_KERNEL_MANIFEST: true run: | BUILD_TYPE="Release" diff --git a/.github/workflows/shell-app-android.yml b/.github/workflows/shell-app-android.yml index 4b84bd685b1e3..0b64b1d89d7d1 100644 --- a/.github/workflows/shell-app-android.yml +++ b/.github/workflows/shell-app-android.yml @@ -48,21 +48,21 @@ jobs: - uses: actions/cache@v2 id: cache-android-ndk with: - path: /usr/local/lib/android/sdk/ndk/19.2.5345600/ - key: ${{ runner.os }}-ndk-19.2.5345600 + path: /usr/local/lib/android/sdk/ndk/21.4.7075529/ + key: ${{ runner.os }}-ndk-21.4.7075529 restore-keys: | ${{ runner.os }}-ndk- - name: Install NDK if: steps.cache-android-ndk.outputs.cache-hit != 'true' run: | - sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;19.2.5345600" + sudo $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "ndk;21.4.7075529" - name: Build Android packages env: - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ run: expotools android-build-packages --packages all - name: Clean Android build artifacts that would needlessly bloat the shell app env: - ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/19.2.5345600/ + ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/21.4.7075529/ run: ./gradlew clean working-directory: android - name: Build shell app tarball diff --git a/android/ReactAndroid/Android-prebuilt.mk b/android/ReactAndroid/Android-prebuilt.mk index 6a921100744dc..18f8c26620458 100644 --- a/android/ReactAndroid/Android-prebuilt.mk +++ b/android/ReactAndroid/Android-prebuilt.mk @@ -64,6 +64,18 @@ LOCAL_EXPORT_C_INCLUDES := $(THIRD_PARTY_NDK_DIR)/glog/exported LOCAL_SHARED_LIBRARIES := libglog include $(PREBUILT_SHARED_LIBRARY) +# yoga +include $(CLEAR_VARS) +LOCAL_MODULE := yoga +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libyoga.so +LOCAL_EXPORT_C_INCLUDES := \ + $(FIRST_PARTY_NDK_DIR)/yogajni/jni \ + $(REACT_COMMON_DIR)/yoga +# Note: Sync with yogajni/Android.mk +LOCAL_CFLAGS += -fvisibility=hidden -fexceptions -frtti -O3 +LOCAL_LDLIBS += -landroid -llog +include $(PREBUILT_SHARED_LIBRARY) + # react_nativemodule_core include $(CLEAR_VARS) LOCAL_MODULE := react_nativemodule_core @@ -85,10 +97,77 @@ LOCAL_EXPORT_C_INCLUDES := \ $(REACT_ANDROID_SRC_DIR)/java/com/facebook/react/turbomodule/core/jni include $(PREBUILT_SHARED_LIBRARY) -# react_codegen_reactandroidspec +# react_render_core +include $(CLEAR_VARS) +LOCAL_MODULE := react_render_core +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_core.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR) \ + $(REACT_COMMON_DIR)/react/renderer/core +include $(PREBUILT_SHARED_LIBRARY) + +# react_render_debug +include $(CLEAR_VARS) +LOCAL_MODULE := react_render_debug +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_debug.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/debug +include $(PREBUILT_SHARED_LIBRARY) + +# react_render_graphics +include $(CLEAR_VARS) +LOCAL_MODULE := react_render_graphics +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_graphics.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/graphics \ + $(REACT_COMMON_DIR)/react/renderer/graphics/platform/cxx +include $(PREBUILT_SHARED_LIBRARY) + +# react_render_imagemanager +include $(CLEAR_VARS) +LOCAL_MODULE := react_render_imagemanager +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_imagemanager.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/imagemanager \ + $(REACT_COMMON_DIR)/react/renderer/imagemanager/platform/cxx +include $(PREBUILT_SHARED_LIBRARY) + +# react_render_mounting +include $(CLEAR_VARS) +LOCAL_MODULE := react_render_mounting +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_mounting.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/mounting +include $(PREBUILT_SHARED_LIBRARY) + +# react_render_mapbuffer +include $(CLEAR_VARS) +LOCAL_MODULE := react_render_mapbuffer +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_mapbuffer.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/mapbuffer +include $(PREBUILT_SHARED_LIBRARY) + +# rrc_view +include $(CLEAR_VARS) +LOCAL_MODULE := rrc_view +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/librrc_view.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/components/view +include $(PREBUILT_SHARED_LIBRARY) + +# jsi +include $(CLEAR_VARS) +LOCAL_MODULE := jsi +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libjsi.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/jsi +include $(PREBUILT_SHARED_LIBRARY) + +# react_codegen_rncore include $(CLEAR_VARS) -LOCAL_MODULE := react_codegen_reactandroidspec -LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_codegen_reactandroidspec.so +LOCAL_MODULE := react_codegen_rncore +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_codegen_rncore.so LOCAL_EXPORT_C_INCLUDES := \ $(REACT_GENERATED_SRC_DIR)/codegen/jni include $(PREBUILT_SHARED_LIBRARY) diff --git a/android/ReactAndroid/README.md b/android/ReactAndroid/README.md index a920b7615d9b0..d2dd8f2f07282 100644 --- a/android/ReactAndroid/README.md +++ b/android/ReactAndroid/README.md @@ -1,7 +1,7 @@ # Building React Native for Android -See the [docs on the website](https://reactnative.dev/docs/building-from-source.html#android). +See the [docs on the wiki](https://github.com/facebook/react-native/wiki/Building-from-source#prerequisites). # Running tests -When you submit a pull request CircleCI will automatically run all tests. To run tests locally, see [Testing](https://reactnative.dev/docs/testing.html). +When you submit a pull request CircleCI will automatically run all tests. To run tests locally, see [Testing](https://github.com/facebook/react-native/wiki/Tests). diff --git a/android/ReactAndroid/build.gradle b/android/ReactAndroid/build.gradle index f9b541cf0958d..50079f4674173 100644 --- a/android/ReactAndroid/build.gradle +++ b/android/ReactAndroid/build.gradle @@ -7,8 +7,8 @@ plugins { id("com.android.library") - id("com.facebook.react.codegen") - id("maven") + id("com.facebook.react") + id("maven-publish") id("de.undercouch.download") } @@ -18,6 +18,7 @@ import de.undercouch.gradle.tasks.download.Download import org.apache.tools.ant.taskdefs.condition.Os import org.apache.tools.ant.filters.ReplaceTokens +def AAR_OUTPUT_URL = "file:${System.env.HOME}/.m2/repository" // We download various C++ open-source dependencies into downloads. // We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk. // After that we build native code from src/main/jni with module path pointing at third-party-ndk. @@ -33,11 +34,6 @@ def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") // - glog-0.3.5 def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES") -// The 'USE_FABRIC' environment variable will build Fabric C++ code into the bundle -// USE_FABRIC=0 will build RN excluding fabric -// USE_FABRIC=1 will build RN including fabric -def enableFabric = (System.getenv('USE_FABRIC') ?: '0').toBoolean() - // The Boost library is a very large download (>100MB). // If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable // and the build will use that. @@ -46,16 +42,6 @@ def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH") // Setup build type for NDK, supported values: {debug, release} def nativeBuildType = System.getenv("NATIVE_BUILD_TYPE") ?: "release" -// A temporary workaround for build failing on some machines. -// See: https://github.com/facebook/react-native/issues/28298 -def follyReplaceContent = ''' - ssize_t r; - do { - r = open(name, flags, mode); - } while (r == -1 && errno == EINTR); - return r; -''' - task createNativeDepsDirectories { downloadsDir.mkdirs() thirdPartyNdkDir.mkdirs() @@ -70,8 +56,8 @@ task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost], type: Copy) { from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest))) - from("src/main/jni/third-party/boost/Android.mk") - include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp") + from("src/main/jni/third-party/boost") + include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp", "asm/**/*.S") includeEmptyDirs = false into("$thirdPartyNdkDir/boost") doLast { @@ -107,13 +93,55 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy from("src/main/jni/third-party/folly/Android.mk") include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk") eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") } - // Fixes problem with Folly failing to build on certain systems. - // See https://github.com/facebook/react-native/issues/28298 - filter { line -> line.replaceAll('return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);', follyReplaceContent) } includeEmptyDirs = false into("$thirdPartyNdkDir/folly") } +task downloadFmt(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/fmtlib/fmt/archive/${FMT_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "fmt-${FMT_VERSION}.tar.gz")) +} + +task prepareFmt(dependsOn: dependenciesPath ? [] : [downloadFmt], type: Copy) { + from(dependenciesPath ?: tarTree(downloadFmt.dest)) + from("src/main/jni/third-party/fmt/Android.mk") + include("fmt-${FMT_VERSION}/src/**/*", "fmt-${FMT_VERSION}/include/**/*", "Android.mk") + eachFile { fname -> fname.path = (fname.path - "fmt-${FMT_VERSION}/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/fmt") +} + +task downloadLibevent(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}-stable/libevent-${LIBEVENT_VERSION}-stable.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "libevent-${LIBEVENT_VERSION}.tar.gz")) +} + +task prepareLibevent(dependsOn: dependenciesPath ? [] : [downloadLibevent], type: Copy) { + from(dependenciesPath ?: tarTree(downloadLibevent.dest)) + from("src/main/jni/third-party/libevent/Android.mk") + from("src/main/jni/third-party/libevent/event-config.h") + from("src/main/jni/third-party/libevent/evconfig-private.h") + include( + "libevent-${LIBEVENT_VERSION}-stable/*.c", + "libevent-${LIBEVENT_VERSION}-stable/*.h", + "libevent-${LIBEVENT_VERSION}-stable/include/**/*", + "evconfig-private.h", + "event-config.h", + "Android.mk" + ) + eachFile { fname -> fname.path = (fname.path - "libevent-${LIBEVENT_VERSION}-stable/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/libevent") + + doLast { + ant.move(file: "$thirdPartyNdkDir/libevent/event-config.h", tofile: "$thirdPartyNdkDir/libevent/include/event2/event-config.h") + } +} + task prepareHermes(dependsOn: createNativeDepsDirectories, type: Copy) { def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") if (!hermesPackagePath) { @@ -142,6 +170,7 @@ task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { // Prepare glog sources to be compiled, this task will perform steps that normally should've been // executed by automake. This way we can avoid dependencies on make/automake task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) { + duplicatesStrategy("warn") from(dependenciesPath ?: tarTree(downloadGlog.dest)) from("src/main/jni/third-party/glog/") include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h") @@ -214,6 +243,8 @@ task downloadNdkBuildDependencies { dependsOn(downloadDoubleConversion) dependsOn(downloadFolly) dependsOn(downloadGlog) + dependsOn(downloadFmt) + dependsOn(downloadLibevent) } /** @@ -260,39 +291,34 @@ def getNdkBuildName() { } def findNdkBuildFullPath() { + // android.ndkDirectory should return project.android.ndkVersion ndkDirectory + def ndkDir = android.ndkDirectory ? android.ndkDirectory.absolutePath : null + if (ndkDir) { + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + // we allow to provide full path to ndk-build tool if (hasProperty("ndk.command")) { return property("ndk.command") } // or just a path to the containing directory if (hasProperty("ndk.path")) { - def ndkDir = property("ndk.path") + ndkDir = property("ndk.path") return new File(ndkDir, getNdkBuildName()).getAbsolutePath() } + // @TODO ANDROID_NDK && ndk.dir is deprecated and will be removed in the future. if (System.getenv("ANDROID_NDK_HOME") != null) { - def ndkDir = System.getenv("ANDROID_NDK_HOME") + ndkDir = System.getenv("ANDROID_NDK_HOME") return new File(ndkDir, getNdkBuildName()).getAbsolutePath() } // Left for the legacy reasons, use the previous one. if (System.getenv("ANDROID_NDK") != null) { - def ndkDir = System.getenv("ANDROID_NDK") + ndkDir = System.getenv("ANDROID_NDK") return new File(ndkDir, getNdkBuildName()).getAbsolutePath() } - try { - def ndkDir = android.ndkDirectory ? android.ndkDirectory.absolutePath : null - if (ndkDir) { - return new File(ndkDir, getNdkBuildName()).getAbsolutePath() - } - } catch (InvalidUserDataException e) { - throw new GradleScriptException( - "Default side-by-side NDK installation is not found.\n" + - "Set \$ANDROID_NDK_HOME environment variable correctly or setup ndk.dir in local.properties.", e - ) - } - return null } @@ -306,6 +332,15 @@ def reactNativeInspectorProxyPort() { return value != null ? value : reactNativeDevServerPort() } +def reactNativeArchitectures() { + if (System.getenv('NDK_ABI_FILTERS')) { return System.getenv('NDK_ABI_FILTERS'); } + def isDebug = gradle.startParameter.taskRequests.any { + it.args.any { it.endsWith("Debug") } + } + def value = project.getProperties().get("reactNativeDebugArchitectures") + return value != null && isDebug ? value : "all" +} + def getNdkBuildFullPath() { def ndkBuildFullPath = findNdkBuildFullPath() if (ndkBuildFullPath == null) { @@ -326,14 +361,17 @@ def getNdkBuildFullPath() { } def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { - dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles) + dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFmt, prepareFolly, prepareGlog, prepareLibevent, extractAARHeaders, extractJNIFiles) dependsOn("generateCodegenArtifactsFromSchema"); inputs.dir("$projectDir/../ReactCommon") inputs.dir("src/main/jni") + inputs.dir("src/main/java/com/facebook/react/turbomodule/core/jni") inputs.dir("src/main/java/com/facebook/react/modules/blob") outputs.dir("$buildDir/react-ndk/all") - commandLine(getNdkBuildFullPath(), + def commandLineArgs = [ + getNdkBuildFullPath(), + "APP_ABI=${reactNativeArchitectures()}", "NDK_DEBUG=" + (nativeBuildType.equalsIgnoreCase("debug") ? "1" : "0"), "NDK_PROJECT_PATH=null", "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk", @@ -343,10 +381,14 @@ def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { "REACT_COMMON_DIR=$projectDir/../ReactCommon", "REACT_GENERATED_SRC_DIR=$buildDir/generated/source", "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react", - "BUILD_FABRIC=$enableFabric", "-C", file("src/main/jni/react/jni").absolutePath, "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors() - ) + ] + if (Os.isFamily(Os.FAMILY_MAC)) { + // This flag will suppress "fcntl(): Bad file descriptor" warnings on local builds. + commandLineArgs.add("--output-sync=none") + } + commandLine(commandLineArgs) } def cleanReactNdkLib = tasks.register("cleanReactNdkLib", Exec) { @@ -406,16 +448,19 @@ task extractJNIFiles { } } +task installArchives { + dependsOn("publishReleasePublicationToNpmRepository") +} + android { compileSdkVersion 30 + ndkVersion ANDROID_NDK_VERSION + if (ANDROID_NDK_PATH != null) { + ndkPath ANDROID_NDK_PATH + } resourcePrefix 'reactandroid_' - compileOptions { - sourceCompatibility(JavaVersion.VERSION_1_8) - targetCompatibility(JavaVersion.VERSION_1_8) - } - defaultConfig { minSdkVersion(21) targetSdkVersion(28) @@ -426,6 +471,7 @@ android { buildConfigField("boolean", "IS_INTERNAL_BUILD", "false") buildConfigField("int", "EXOPACKAGE_FLAGS", "0") + buildConfigField("int", "HERMES_BYTECODE_VERSION", "0") resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort() resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort() @@ -445,11 +491,7 @@ android { } } - tasks.withType(JavaCompile) { - compileTask -> - compileTask.dependsOn(packageReactNdkLibs) - } - + preBuild.dependsOn(packageReactNdkLibs) clean.dependsOn(cleanReactNdkLib) lintOptions { @@ -464,25 +506,30 @@ android { configurations { extractHeaders extractJNI + javadocDeps.extendsFrom api } } dependencies { - api("com.facebook.infer.annotation:infer-annotation:0.11.2") - api("com.facebook.yoga:proguard-annotations:1.14.1") + api("com.facebook.infer.annotation:infer-annotation:0.18.0") + api("com.facebook.yoga:proguard-annotations:1.19.0") api("javax.inject:javax.inject:1") api("androidx.appcompat:appcompat:1.2.0") + api("androidx.autofill:autofill:1.1.0") api("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") api("com.facebook.fresco:fresco:${FRESCO_VERSION}") api("com.facebook.fresco:imagepipeline-okhttp3:${FRESCO_VERSION}") + api("com.facebook.fresco:ui-common:${FRESCO_VERSION}") api("com.facebook.soloader:soloader:${SO_LOADER_VERSION}") api("com.google.code.findbugs:jsr305:3.0.2") api("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}") api("com.squareup.okhttp3:okhttp-urlconnection:${OKHTTP_VERSION}") - api("com.squareup.okio:okio:1.17.5") - api("com.facebook.fbjni:fbjni-java-only:0.0.3") - extractHeaders("com.facebook.fbjni:fbjni:0.0.2:headers") - extractJNI("com.facebook.fbjni:fbjni:0.0.2") + api("com.squareup.okio:okio:2.9.0") + api("com.facebook.fbjni:fbjni-java-only:0.2.2") + extractHeaders("com.facebook.fbjni:fbjni:0.2.2:headers") + extractJNI("com.facebook.fbjni:fbjni:0.2.2") + + javadocDeps("com.squareup:javapoet:1.13.0") testImplementation("junit:junit:${JUNIT_VERSION}") testImplementation("org.powermock:powermock-api-mockito2:${POWERMOCK_VERSION}") @@ -498,10 +545,62 @@ dependencies { androidTestImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}") } -apply(from: "release.gradle") - react { + // TODO: The library name is chosen for parity with Fabric components & iOS + // This should be changed to a more generic name, e.g. `ReactCoreSpec`. + libraryName = "rncore" jsRootDir = file("$projectDir/../../react-native-lab/react-native/Libraries") - reactNativeRootDir = file("$projectDir/../../react-native-lab/react-native") + codegenDir = file("$projectDir/../../react-native-lab/react-native/packages/react-native-codegen") + reactRoot = file("$projectDir/../../react-native-lab/react-native") useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false } + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + // Applies the component for the release build variant. + from components.release + + // You can then customize attributes of the publication as shown below. + artifactId = POM_ARTIFACT_ID + groupId = GROUP + version = '45.0.0' + + pom { + name = POM_NAME + description = "A framework for building native apps with React" + url = "https://github.com/facebook/react-native" + + developers { + developer { + id = "facebook" + name = "Facebook" + } + } + + licenses { + license { + name = "MIT License" + url = "https://github.com/facebook/react-native/blob/HEAD/LICENSE" + distribution = "repo" + } + } + + scm { + url = "https://github.com/facebook/react-native.git" + connection = "scm:git:https://github.com/facebook/react-native.git" + developerConnection = "scm:git:git@github.com:facebook/react-native.git" + } + } + } + } + + repositories { + maven { + name = "npm" + url = AAR_OUTPUT_URL + } + } + } +} diff --git a/android/ReactAndroid/gradle.properties b/android/ReactAndroid/gradle.properties index d4e629732bdca..432839ab31a91 100644 --- a/android/ReactAndroid/gradle.properties +++ b/android/ReactAndroid/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=0.64.2 +VERSION_NAME=0.67.2 GROUP=com.facebook.react POM_NAME=ReactNative @@ -11,13 +11,15 @@ ROBOLECTRIC_VERSION=4.4 JUNIT_VERSION=4.12 ANDROIDX_TEST_VERSION=1.1.0 -FRESCO_VERSION=2.0.0 -OKHTTP_VERSION=3.12.12 -SO_LOADER_VERSION=0.9.0 +FRESCO_VERSION=2.5.0 +OKHTTP_VERSION=4.9.2 +SO_LOADER_VERSION=0.10.1 BOOST_VERSION=1_63_0 DOUBLE_CONVERSION_VERSION=1.1.6 -FOLLY_VERSION=2020.01.13.00 +FOLLY_VERSION=2021.06.28.00 +FMT_VERSION=6.2.1 +LIBEVENT_VERSION=2.1.12 GLOG_VERSION=0.3.5 android.useAndroidX=true diff --git a/android/ReactAndroid/proguard-rules.pro b/android/ReactAndroid/proguard-rules.pro index e13dfef7ec70a..93a74c617b83f 100644 --- a/android/ReactAndroid/proguard-rules.pro +++ b/android/ReactAndroid/proguard-rules.pro @@ -50,17 +50,11 @@ -dontwarn com.facebook.react.** -keep,includedescriptorclasses class com.facebook.react.bridge.** { *; } +-keep,includedescriptorclasses class com.facebook.react.turbomodule.core.** { *; } # hermes -keep class com.facebook.jni.** { *; } -# okhttp - --keepattributes Signature --keepattributes *Annotation* --keep class okhttp3.** { *; } --keep interface okhttp3.** { *; } --dontwarn okhttp3.** # okio diff --git a/android/ReactAndroid/release.gradle b/android/ReactAndroid/release.gradle deleted file mode 100644 index 115828f627043..0000000000000 --- a/android/ReactAndroid/release.gradle +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -apply(plugin: "maven") -apply(plugin: "signing") - -ext { - AAR_OUTPUT_URL = "file://${projectDir}/../android" -} - -// Gradle tasks for publishing to maven -// 1) To install in local maven repo use :installArchives task -// 2) To upload artifact to maven central use: :uploadArchives (you'd need to have the permission to do that) - -def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false -} - -def getRepositoryUrl() { - return project.findProperty("repositoryUrl") ?: "https://oss.sonatype.org/service/local/staging/deploy/maven2/" -} - -def getRepositoryUsername() { - return project.findProperty("repositoryUsername") ?: "" -} - -def getRepositoryPassword() { - return project.findProperty("repositoryPassword") ?: "" -} - -def configureReactNativePom(def pom) { - pom.project { - name(POM_NAME) - artifactId(POM_ARTIFACT_ID) - packaging(POM_PACKAGING) - description("A framework for building native apps with React") - url("https://github.com/facebook/react-native") - - scm { - url("https://github.com/facebook/react-native.git") - connection("scm:git:https://github.com/facebook/react-native.git") - developerConnection("scm:git:git@github.com:facebook/react-native.git") - } - - licenses { - license { - name("MIT License") - url("https://github.com/facebook/react-native/blob/master/LICENSE") - distribution("repo") - } - } - - developers { - developer { - id("facebook") - name("Facebook") - } - } - } -} - -if (JavaVersion.current().isJava8Compatible()) { - allprojects { - tasks.withType(Javadoc) { - options.addStringOption("Xdoclint:none", "-quiet") - } - } -} - -afterEvaluate { project -> - - task androidJavadoc(type: Javadoc, dependsOn: generateReleaseBuildConfig) { - source = android.sourceSets.main.java.srcDirs - classpath += files(android.bootClasspath) - classpath += files(project.getConfigurations().getByName("compile").asList()) - classpath += files("$buildDir/generated/source/buildConfig/release") - include("**/*.java") - } - - task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) { - classifier = "javadoc" - from(androidJavadoc.destinationDir) - } - - task androidSourcesJar(type: Jar) { - classifier = "sources" - from(android.sourceSets.main.java.srcDirs) - include("**/*.java") - } - - android.libraryVariants.all { variant -> - def name = variant.name.capitalize() - task "jar${name}"(type: Jar, dependsOn: variant.javaCompileProvider.get()) { - from(variant.javaCompileProvider.get().destinationDir) - } - } - - artifacts { - archives(androidSourcesJar) - archives(androidJavadocJar) - } - - version = '44.0.0' - group = 'com.facebook.react' - - signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } - sign(configurations.archives) - } - - uploadArchives { - configuration = configurations.archives - repositories.mavenDeployer { - beforeDeployment { - MavenDeployment deployment -> signing.signPom(deployment) - } - - repository(url: getRepositoryUrl()) { - authentication( - userName: getRepositoryUsername(), - password: getRepositoryPassword()) - - } - - configureReactNativePom(pom) - } - } - - task installArchives(type: Upload) { - configuration = configurations.archives - repositories.mavenDeployer { - // Deploy to react-native/android, ready to publish to npm - repository(url: AAR_OUTPUT_URL) - - configureReactNativePom(pom) - } - } -} diff --git a/android/ReactAndroid/src/androidTest/buck-runner/BUCK b/android/ReactAndroid/src/androidTest/buck-runner/BUCK index 0bad88bdeb83e..c27596ee462c3 100644 --- a/android/ReactAndroid/src/androidTest/buck-runner/BUCK +++ b/android/ReactAndroid/src/androidTest/buck-runner/BUCK @@ -11,6 +11,7 @@ rn_android_binary( name = "instrumentation-tests", keystore = KEYSTORE_TARGET, manifest = "AndroidManifest.xml", + use_split_dex = True, deps = [ react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"), react_native_dep("third-party/java/testing-support-lib:exposed-instrumentation-api"), diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK index 6d16a702ecf40..78e1dd6ae74fa 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK @@ -9,6 +9,7 @@ rn_android_library( "network/**/*.java", ], ), + autoglob = False, is_androidx = True, visibility = [ "PUBLIC", @@ -26,7 +27,7 @@ rn_android_library( react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), react_native_android_toplevel_dep("third-party/java/mockito2:mockito2"), - react_native_dep("third-party/java/testing-support-lib:runner"), + react_native_dep("third-party/android/androidx:test-runner"), react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), react_native_integration_tests_target("java/com/facebook/react/testing/network:network"), react_native_target("java/com/facebook/react:react"), diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java index 4150b833ee5f2..036a2d5abcfe5 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java @@ -37,7 +37,7 @@ protected void setUp() throws Exception { final ReactAppTestActivity activity = getActivity(); activity.loadApp( getReactApplicationKeyUnderTest(), createReactInstanceSpecForTest(), getEnableDevSupport()); - waitForBridgeAndUIIdle(); + waitForBridgeAndUIIdle(5000); } @Override diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK index 80a28b690ef50..6a540efc6d6e3 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/BUCK @@ -3,12 +3,13 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "idledetection", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, visibility = [ "PUBLIC", ], deps = [ - react_native_dep("third-party/java/testing-support-lib:runner"), + react_native_dep("third-party/android/androidx:test-runner"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/modules/core:core"), ], diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java index e7410dc91b0db..4cdbf83a058dd 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/idledetection/ReactIdleDetectionUtil.java @@ -79,7 +79,7 @@ public void doFrame(long frameTimeNanos) { } private static void waitForJSIdle(ReactContext reactContext) { - if (!reactContext.hasActiveCatalystInstance()) { + if (!reactContext.hasActiveReactInstance()) { return; } final CountDownLatch latch = new CountDownLatch(1); diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK index 980eda5d8d060..d8e68b3f29aa6 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "network", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/rule/BUCK b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/rule/BUCK index 1bf0b3c7a8ab1..d410f7c5f3a09 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/rule/BUCK +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/testing/rule/BUCK @@ -1,4 +1,3 @@ -# BUILD FILE SYNTAX: SKYLARK load( "//tools/build_defs/oss:rn_defs.bzl", "IS_OSS_BUILD", @@ -11,21 +10,23 @@ load( rn_android_library( name = "rule", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, visibility = [ "PUBLIC", ], - deps = ([ + deps = [ react_native_dep("third-party/android/androidx:test-espresso-core"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/testing-support-lib:testing-support-lib"), react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/android/androidx:appcompat"), react_native_dep("third-party/android/androidx:core"), react_native_dep("third-party/android/androidx:fragment"), react_native_dep("third-party/android/androidx:legacy-support-core-ui"), react_native_dep("third-party/android/androidx:legacy-support-core-utils"), + react_native_dep("third-party/android/androidx:test-rules"), + react_native_dep("third-party/android/androidx:test-runner"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_integration_tests_target("java/com/facebook/react/testing:testing"), react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"), @@ -35,8 +36,8 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/core:core"), react_native_target("java/com/facebook/react/shell:shell"), react_native_target("java/com/facebook/react/uimanager:uimanager"), - ]) + ([ + ] + [ react_native_dep("java/com/facebook/testing/instrumentation:instrumentation"), react_native_dep("java/com/facebook/testing/instrumentation/base:base"), - ]) if not IS_OSS_BUILD else [], + ] if not IS_OSS_BUILD else [], ) diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 0b29ef31268c2..131dd4af63c3a 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_int rn_android_library( name = "tests", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, visibility = [ "PUBLIC", @@ -24,7 +25,6 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/appstate:appstate"), react_native_target("java/com/facebook/react/modules/core:core"), - react_native_target("java/com/facebook/react/modules/datepicker:datepicker"), react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"), react_native_target("java/com/facebook/react/modules/share:share"), react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"), @@ -32,7 +32,6 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager/util:util"), - react_native_target("java/com/facebook/react/views/picker:picker"), react_native_target("java/com/facebook/react/views/progressbar:progressbar"), react_native_target("java/com/facebook/react/views/scroll:scroll"), react_native_target("java/com/facebook/react/views/slider:slider"), diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/DatePickerDialogTestCase.java b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/DatePickerDialogTestCase.java deleted file mode 100644 index 93d3568fa24e2..0000000000000 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/DatePickerDialogTestCase.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.tests; - -import android.app.DatePickerDialog; -import android.content.DialogInterface; -import android.widget.DatePicker; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; -import com.facebook.react.bridge.BaseJavaModule; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.facebook.react.modules.datepicker.DatePickerDialogModule; -import com.facebook.react.testing.ReactAppInstrumentationTestCase; -import com.facebook.react.testing.ReactInstanceSpecForTest; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -/** Test case for {@link DatePickerDialogModule} options and callbacks. */ -public class DatePickerDialogTestCase extends ReactAppInstrumentationTestCase { - - private static interface DatePickerDialogTestModule extends JavaScriptModule { - public void showDatePickerDialog(WritableMap options); - } - - private static class DatePickerDialogRecordingModule extends BaseJavaModule { - - private final List mDates = new ArrayList(); - private int mDismissed = 0; - private int mErrors = 0; - - @Override - public String getName() { - return "DatePickerDialogRecordingModule"; - } - - @ReactMethod - public void recordDate(int year, int month, int day) { - mDates.add(new Integer[] {year, month, day}); - } - - @ReactMethod - public void recordDismissed() { - mDismissed++; - } - - @ReactMethod - public void recordError() { - mErrors++; - } - - public List getDates() { - return new ArrayList(mDates); - } - - public int getDismissed() { - return mDismissed; - } - - public int getErrors() { - return mErrors; - } - } - - final DatePickerDialogRecordingModule mRecordingModule = new DatePickerDialogRecordingModule(); - - @Override - protected ReactInstanceSpecForTest createReactInstanceSpecForTest() { - return super.createReactInstanceSpecForTest().addNativeModule(mRecordingModule); - } - - @Override - protected String getReactApplicationKeyUnderTest() { - return "DatePickerDialogTestApp"; - } - - private static long getDateInMillis(int year, int month, int date) { - final Calendar c = Calendar.getInstance(); - c.set(Calendar.YEAR, year); - c.set(Calendar.MONTH, month); - c.set(Calendar.DATE, date); - return c.getTimeInMillis(); - } - - private DatePickerDialogTestModule getTestModule() { - return getReactContext().getCatalystInstance().getJSModule(DatePickerDialogTestModule.class); - } - - private DialogFragment showDialog(WritableMap options) { - getTestModule().showDatePickerDialog(options); - - waitForBridgeAndUIIdle(); - getInstrumentation().waitForIdleSync(); - - return (DialogFragment) - getActivity() - .getSupportFragmentManager() - .findFragmentByTag(DatePickerDialogModule.FRAGMENT_TAG); - } - - public void testShowBasicDatePicker() { - final Fragment datePickerFragment = showDialog(null); - - assertNotNull(datePickerFragment); - } - - public void testPresetDate() { - final WritableMap options = new WritableNativeMap(); - options.putDouble("date", getDateInMillis(2020, 5, 6)); - - final DialogFragment datePickerFragment = showDialog(options); - final DatePicker datePicker = - ((DatePickerDialog) datePickerFragment.getDialog()).getDatePicker(); - - assertEquals(2020, datePicker.getYear()); - assertEquals(5, datePicker.getMonth()); - assertEquals(6, datePicker.getDayOfMonth()); - } - - public void testCallback() throws Throwable { - final WritableMap options = new WritableNativeMap(); - options.putDouble("date", getDateInMillis(2020, 5, 6)); - - final DialogFragment datePickerFragment = showDialog(options); - - runTestOnUiThread( - new Runnable() { - @Override - public void run() { - ((DatePickerDialog) datePickerFragment.getDialog()) - .getButton(DialogInterface.BUTTON_POSITIVE) - .performClick(); - } - }); - - getInstrumentation().waitForIdleSync(); - waitForBridgeAndUIIdle(); - - assertEquals(0, mRecordingModule.getErrors()); - assertEquals(1, mRecordingModule.getDates().size()); - assertEquals(2020, (int) mRecordingModule.getDates().get(0)[0]); - assertEquals(5, (int) mRecordingModule.getDates().get(0)[1]); - assertEquals(6, (int) mRecordingModule.getDates().get(0)[2]); - } - - public void testDismissCallback() throws Throwable { - final DialogFragment datePickerFragment = showDialog(null); - - runTestOnUiThread( - new Runnable() { - @Override - public void run() { - datePickerFragment.getDialog().dismiss(); - } - }); - - getInstrumentation().waitForIdleSync(); - waitForBridgeAndUIIdle(); - - assertEquals(0, mRecordingModule.getErrors()); - assertEquals(0, mRecordingModule.getDates().size()); - assertEquals(1, mRecordingModule.getDismissed()); - } -} diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ReactPickerTestCase.java b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ReactPickerTestCase.java deleted file mode 100644 index c301b04e7e8b9..0000000000000 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/ReactPickerTestCase.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.tests; - -import android.graphics.Color; -import android.widget.Spinner; -import android.widget.SpinnerAdapter; -import android.widget.TextView; -import com.facebook.react.bridge.BaseJavaModule; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.testing.ReactAppInstrumentationTestCase; -import com.facebook.react.testing.ReactInstanceSpecForTest; -import com.facebook.react.views.picker.ReactDialogPickerManager; -import com.facebook.react.views.picker.ReactDropdownPickerManager; -import com.facebook.react.views.picker.ReactPicker; -import com.facebook.react.views.picker.ReactPickerManager; -import java.util.ArrayList; -import java.util.List; - -/** - * Integration test for {@link ReactDialogPickerManager} and {@link ReactDropdownPickerManager} - * (and, implicitly, {@link ReactPickerManager}). Tests basic properties, events and switching - * between spinner modes (which changes the used manager). - */ -public class ReactPickerTestCase extends ReactAppInstrumentationTestCase { - - private static interface PickerAndroidTestModule extends JavaScriptModule { - public void selectItem(int position); - - public void setMode(String mode); - - public void setPrimaryColor(String color); - } - - public static class PickerAndroidRecordingModule extends BaseJavaModule { - private final List mSelections = new ArrayList(); - - @Override - public String getName() { - return "PickerAndroidRecordingModule"; - } - - @ReactMethod - public void recordSelection(int position) { - mSelections.add(position); - } - - public List getSelections() { - return new ArrayList(mSelections); - } - } - - private PickerAndroidRecordingModule mRecordingModule; - - @Override - protected String getReactApplicationKeyUnderTest() { - return "PickerAndroidTestApp"; - } - - @Override - protected ReactInstanceSpecForTest createReactInstanceSpecForTest() { - mRecordingModule = new PickerAndroidRecordingModule(); - return super.createReactInstanceSpecForTest().addNativeModule(mRecordingModule); - } - - public void testBasicProperties() { - ReactPicker spinner = getViewAtPath(0, 0); - SpinnerAdapter adapter = spinner.getAdapter(); - - assertEquals(Spinner.MODE_DIALOG, spinner.getMode()); - assertEquals("prompt", spinner.getPrompt()); - assertNotNull(adapter); - assertEquals(3, adapter.getCount()); - assertEquals("item1", ((TextView) adapter.getView(0, null, null)).getText()); - assertEquals("item2", ((TextView) adapter.getView(1, null, null)).getText()); - assertEquals("item3", ((TextView) adapter.getView(2, null, null)).getText()); - assertEquals(1, spinner.getSelectedItemPosition()); - - // test colors - assertEquals(Color.RED, ((TextView) adapter.getView(0, null, null)).getCurrentTextColor()); - assertEquals(Color.GREEN, ((TextView) adapter.getView(1, null, null)).getCurrentTextColor()); - assertEquals(Color.BLUE, ((TextView) adapter.getView(2, null, null)).getCurrentTextColor()); - assertEquals( - Color.RED, ((TextView) adapter.getDropDownView(0, null, null)).getCurrentTextColor()); - assertEquals( - Color.GREEN, ((TextView) adapter.getDropDownView(1, null, null)).getCurrentTextColor()); - assertEquals( - Color.BLUE, ((TextView) adapter.getDropDownView(2, null, null)).getCurrentTextColor()); - getTestModule().setPrimaryColor("black"); - waitForBridgeAndUIIdle(); - assertEquals(Color.BLACK, ((TextView) adapter.getView(0, null, null)).getCurrentTextColor()); - assertEquals(Color.BLACK, ((TextView) adapter.getView(1, null, null)).getCurrentTextColor()); - assertEquals(Color.BLACK, ((TextView) adapter.getView(2, null, null)).getCurrentTextColor()); - assertEquals( - Color.RED, ((TextView) adapter.getDropDownView(0, null, null)).getCurrentTextColor()); - assertEquals( - Color.GREEN, ((TextView) adapter.getDropDownView(1, null, null)).getCurrentTextColor()); - assertEquals( - Color.BLUE, ((TextView) adapter.getDropDownView(2, null, null)).getCurrentTextColor()); - } - - public void testDropdownPicker() { - ReactPicker spinner = getViewAtPath(0, 1); - - assertEquals(Spinner.MODE_DROPDOWN, spinner.getMode()); - } - - public void testDisabledPicker() { - ReactPicker spinner = getViewAtPath(0, 2); - - assertFalse(spinner.isEnabled()); - } - - public void testUpdateSelectedItem() { - ReactPicker spinner = getViewAtPath(0, 0); - assertEquals(1, spinner.getSelectedItemPosition()); - - getTestModule().selectItem(2); - waitForBridgeAndUIIdle(); - getInstrumentation().waitForIdleSync(); - - assertEquals(2, spinner.getSelectedItemPosition()); - } - - public void testUpdateMode() { - ReactPicker spinner = getViewAtPath(0, 1); - assertEquals(Spinner.MODE_DROPDOWN, spinner.getMode()); - - getTestModule().setMode("dialog"); - waitForBridgeAndUIIdle(); - getInstrumentation().waitForIdleSync(); - - // changing the spinner mode in JS actually creates a new component on the native side, as - // there's no way to change the mode once you have constructed a Spinner. - ReactPicker newPicker = getViewAtPath(0, 1); - assertTrue(spinner != newPicker); - assertEquals(Spinner.MODE_DIALOG, newPicker.getMode()); - } - - public void testOnSelect() throws Throwable { - runTestOnUiThread( - new Runnable() { - @Override - public void run() { - ReactPicker spinner = getViewAtPath(0, 0); - spinner.setSelection(2); - } - }); - - getInstrumentation().waitForIdleSync(); - waitForBridgeAndUIIdle(); - - List selections = mRecordingModule.getSelections(); - assertEquals(1, selections.size()); - assertEquals(2, (int) selections.get(0)); - } - - public void testOnSelectSequence() throws Throwable { - updateFirstSpinnerAndCheckLastSpinnerMatches(0); - updateFirstSpinnerAndCheckLastSpinnerMatches(2); - updateFirstSpinnerAndCheckLastSpinnerMatches(0); - updateFirstSpinnerAndCheckLastSpinnerMatches(2); - } - - private void updateFirstSpinnerAndCheckLastSpinnerMatches(final int indexToSelect) - throws Throwable { - // The last spinner has the same selected value as the first one. - // Test that user selection is propagated correctly to JS, to setState, and to Spinners. - runTestOnUiThread( - new Runnable() { - @Override - public void run() { - ReactPicker spinner = getViewAtPath(0, 0); - spinner.setSelection(indexToSelect); - } - }); - getInstrumentation().waitForIdleSync(); - waitForBridgeAndUIIdle(); - - ReactPicker spinnerInSync = getViewAtPath(0, 3); - assertEquals( - "Picker selection was not updated correctly via setState.", - indexToSelect, - spinnerInSync.getSelectedItemPosition()); - } - - private PickerAndroidTestModule getTestModule() { - return getReactContext().getCatalystInstance().getJSModule(PickerAndroidTestModule.class); - } -} diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java index 920ce02181219..88e0cf51e09d7 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java @@ -259,8 +259,7 @@ public void run() { /** * Test that the mentions input has colors displayed correctly. Removed for being flaky in open * source, December 2016 public void testMetionsInputColors() throws Throwable { EventDispatcher - * eventDispatcher = - * getReactContext().getNativeModule(UIManagerModule.class).getEventDispatcher(); ReactEditText + * eventDispatcher = UIManagerHelper.getEventEmitterForReactTag(reactContext, tag); ReactEditText * reactEditText = getViewByTestId("tokenizedInput"); String newText = "#Things and more #things"; * int contentWidth = reactEditText.getWidth(); int contentHeight = reactEditText.getHeight(); int * start = 0; int count = newText.length(); diff --git a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/core/BUCK b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/core/BUCK index 0c2c6a3781d99..8ce6ba218f7c7 100644 --- a/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/core/BUCK +++ b/android/ReactAndroid/src/androidTest/java/com/facebook/react/tests/core/BUCK @@ -1,4 +1,3 @@ -# BUILD FILE SYNTAX: SKYLARK load( "//tools/build_defs/oss:rn_defs.bzl", "IS_OSS_BUILD", @@ -11,12 +10,14 @@ load( rn_android_library( name = "core", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, - deps = ([ + deps = [ react_native_dep("third-party/android/androidx:test-espresso-core"), react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/testing-support-lib:testing-support-lib"), + react_native_dep("third-party/android/androidx:test-rules"), + react_native_dep("third-party/android/androidx:test-runner"), react_native_integration_tests_target("java/com/facebook/react/testing:testing"), react_native_integration_tests_target("java/com/facebook/react/testing/rule:rule"), react_native_target("java/com/facebook/react:react"), @@ -26,7 +27,7 @@ rn_android_library( react_native_target("java/com/facebook/react/module/model:model"), react_native_target("java/com/facebook/react/shell:shell"), react_native_target("java/com/facebook/react/uimanager:uimanager"), - ]) + ([ + ] + [ react_native_dep("java/com/facebook/fbreact/testing:testing"), - ]) if not IS_OSS_BUILD else [], + ] if not IS_OSS_BUILD else [], ) diff --git a/android/ReactAndroid/src/androidTest/js/BUCK b/android/ReactAndroid/src/androidTest/js/BUCK new file mode 100644 index 0000000000000..3820c3470fb07 --- /dev/null +++ b/android/ReactAndroid/src/androidTest/js/BUCK @@ -0,0 +1,26 @@ +load("//tools/build_defs:js_glob.bzl", "js_glob") +load("//tools/build_defs/oss:metro_defs.bzl", "rn_library") + +# This file was generated by running +# js1 build buckfiles + +rn_library( + name = "js", + srcs = js_glob( + [ + "**/*", + ], + excludes = [ + "**/__*__/**", + "**/*.command", + "**/*.md", + ], + ), + labels = ["supermodule:xplat/default/public.react_native.tests"], + skip_processors = True, + visibility = ["PUBLIC"], + deps = [ + "//xplat/js/RKJSModules/vendor/react:react", + "//xplat/js/react-native-github:react-native", + ], +) diff --git a/android/ReactAndroid/src/androidTest/js/DatePickerDialogTestModule.js b/android/ReactAndroid/src/androidTest/js/DatePickerDialogTestModule.js deleted file mode 100644 index 5c23e61a5ec5a..0000000000000 --- a/android/ReactAndroid/src/androidTest/js/DatePickerDialogTestModule.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; - -const React = require('react'); -const {DatePickerAndroid, NativeModules, View} = require('react-native'); -const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge'); - -const {DatePickerDialogRecordingModule: RecordingModule} = NativeModules; - -class DatePickerDialogTestApp extends React.Component { - render() { - return ; - } -} - -const DatePickerDialogTestModule = { - DatePickerDialogTestApp: DatePickerDialogTestApp, - showDatePickerDialog: function(options) { - DatePickerAndroid.open(options).then( - ({action, year, month, day}) => { - if (action === DatePickerAndroid.dateSetAction) { - RecordingModule.recordDate(year, month, day); - } else if (action === DatePickerAndroid.dismissedAction) { - RecordingModule.recordDismissed(); - } - }, - ({code, message}) => RecordingModule.recordError(), - ); - }, -}; - -BatchedBridge.registerCallableModule( - 'DatePickerDialogTestModule', - DatePickerDialogTestModule, -); - -module.exports = DatePickerDialogTestModule; diff --git a/android/ReactAndroid/src/androidTest/js/PickerAndroidTestModule.js b/android/ReactAndroid/src/androidTest/js/PickerAndroidTestModule.js deleted file mode 100644 index a4e0cc0ae67ad..0000000000000 --- a/android/ReactAndroid/src/androidTest/js/PickerAndroidTestModule.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; - -const React = require('react'); -const {NativeModules, Picker, View} = require('react-native'); -const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge'); - -const {Recording: RecordingModule} = NativeModules; -const Item = Picker.Item; - -let appInstance; - -class PickerAndroidTestApp extends React.Component { - state = { - selected: 1, - mode: 'dropdown', - style: {}, - }; - - UNSAFE_componentWillMount() { - appInstance = this; - } - - render() { - return ( - - - - - - - - - - - - - - - - - - - - - ); - } - - onValueChange = value => { - this.setState({selected: value}); - RecordingModule.recordSelection(value); - }; -} - -const PickerAndroidTestModule = { - PickerAndroidTestApp: PickerAndroidTestApp, - selectItem: function(value) { - appInstance.setState({selected: value}); - }, - setMode: function(mode) { - appInstance.setState({mode: mode}); - }, - setPrimaryColor: function(color) { - appInstance.setState({style: {color}}); - }, -}; - -BatchedBridge.registerCallableModule( - 'PickerAndroidTestModule', - PickerAndroidTestModule, -); - -module.exports = PickerAndroidTestModule; diff --git a/android/ReactAndroid/src/androidTest/js/TestBundle.js b/android/ReactAndroid/src/androidTest/js/TestBundle.js index 4608c1780d373..b81937503589a 100644 --- a/android/ReactAndroid/src/androidTest/js/TestBundle.js +++ b/android/ReactAndroid/src/androidTest/js/TestBundle.js @@ -20,11 +20,8 @@ require('./TestJSLocaleModule'); require('./TestJSToJavaParametersModule'); require('./TestJavaToJSReturnValuesModule'); require('./UIManagerTestModule'); - require('./CatalystRootViewTestModule'); -require('./DatePickerDialogTestModule'); require('./MeasureLayoutTestModule'); -require('./PickerAndroidTestModule'); require('./ScrollViewTestModule'); require('./ShareTestModule'); require('./SwipeRefreshLayoutTestModule'); @@ -44,11 +41,6 @@ const apps = [ component: () => require('./CatalystRootViewTestModule').CatalystRootViewTestApp, }, - { - appKey: 'DatePickerDialogTestApp', - component: () => - require('./DatePickerDialogTestModule').DatePickerDialogTestApp, - }, { appKey: 'JSResponderTestApp', component: () => require('./JSResponderTestApp'), @@ -86,10 +78,6 @@ const apps = [ appKey: 'NativeIdTestApp', component: () => require('./NativeIdTestModule').NativeIdTestApp, }, - { - appKey: 'PickerAndroidTestApp', - component: () => require('./PickerAndroidTestModule').PickerAndroidTestApp, - }, { appKey: 'ScrollViewTestApp', component: () => require('./ScrollViewTestModule').ScrollViewTestApp, diff --git a/android/ReactAndroid/src/androidTest/js/UIManagerTestModule.js b/android/ReactAndroid/src/androidTest/js/UIManagerTestModule.js index 61b0d0b2f384d..25c7c5e0cc75c 100644 --- a/android/ReactAndroid/src/androidTest/js/UIManagerTestModule.js +++ b/android/ReactAndroid/src/androidTest/js/UIManagerTestModule.js @@ -16,6 +16,7 @@ const React = require('react'); const renderApplication = require('react-native/Libraries/ReactNative/renderApplication'); const {StyleSheet, Text, View} = require('react-native'); +import type {RootTag} from 'react-native/Libraries/Types/RootTagTypes'; type FlexTestAppProps = $ReadOnly<{||}>; class FlexTestApp extends React.Component { @@ -234,26 +235,26 @@ const UpdatePositionInListTestAppStyles = StyleSheet.create({ const emptyExactProps = Object.freeze({}); const UIManagerTestModule = { - renderFlexTestApplication(rootTag: number) { + renderFlexTestApplication(rootTag: RootTag) { renderApplication(FlexTestApp, emptyExactProps, rootTag); }, - renderFlexWithTextApplication(rootTag: number) { + renderFlexWithTextApplication(rootTag: RootTag) { renderApplication(FlexWithText, emptyExactProps, rootTag); }, - renderAbsolutePositionBottomRightTestApplication(rootTag: number) { + renderAbsolutePositionBottomRightTestApplication(rootTag: RootTag) { renderApplication( AbsolutePositionBottomRightTestApp, emptyExactProps, rootTag, ); }, - renderAbsolutePositionTestApplication(rootTag: number) { + renderAbsolutePositionTestApplication(rootTag: RootTag) { renderApplication(AbsolutePositionTestApp, emptyExactProps, rootTag); }, - renderCenteredTextViewTestApplication(rootTag: number, text: string) { + renderCenteredTextViewTestApplication(rootTag: RootTag, text: string) { renderApplication(CenteredTextView, {text: text}, rootTag); }, - renderUpdatePositionInListTestApplication(rootTag: number) { + renderUpdatePositionInListTestApplication(rootTag: RootTag) { renderApplication(UpdatePositionInListTestApp, emptyExactProps, rootTag); }, flushUpdatePositionInList, diff --git a/android/ReactAndroid/src/main/java/com/facebook/BUCK b/android/ReactAndroid/src/main/java/com/facebook/BUCK index a7461d145d8fc..f24e877ad0ad4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "yoga", srcs = glob(["yoga/*.java"]), + autoglob = False, visibility = ["PUBLIC"], deps = [ react_native_dep("libraries/fbjni:java"), diff --git a/android/ReactAndroid/src/main/java/com/facebook/debug/debugoverlay/model/BUCK b/android/ReactAndroid/src/main/java/com/facebook/debug/debugoverlay/model/BUCK index a161001ec1cab..3ccc5d058d7d8 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/debug/debugoverlay/model/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/debug/debugoverlay/model/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "model", srcs = glob(["*.java"]), + autoglob = False, visibility = [ "PUBLIC", ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK b/android/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK index 2daef176010bf..13c5da59066b2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "holder", srcs = glob(["*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK b/android/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK index 3e2e015c78e8d..2133f0f6b9978 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "tags", srcs = glob(["*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/Android.mk index 3548e86abd3c9..651168243ef04 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/Android.mk @@ -18,8 +18,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-no LOCAL_CPP_FEATURES := exceptions -LOCAL_STATIC_LIBRARIES := libjsireact libjsi -LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes +LOCAL_STATIC_LIBRARIES := libjsireact +LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes libjsi include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK b/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK index 439ae4b474357..ead8aae637e6a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_nat rn_android_library( name = "instrumentation", srcs = ["HermesMemoryDumper.java"], + autoglob = False, visibility = [ "PUBLIC", ], @@ -11,6 +12,7 @@ rn_android_library( rn_android_library( name = "hermes_samplingprofiler", srcs = ["HermesSamplingProfiler.java"], + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = ["PUBLIC"], deps = [ @@ -29,7 +31,8 @@ rn_xplat_cxx_library( ], headers = ["HermesSamplingProfiler.h"], header_namespace = "", - compiler_flags = ["-fexceptions"], + compiler_flags_enable_exceptions = True, # TODO: is this necessary? + compiler_flags_enable_rtti = True, # TODO: is this necessary? labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, soname = "libjsijniprofiler.$(ext)", diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/Android.mk index ff05e4c14824c..7716394c31f78 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/Android.mk @@ -17,8 +17,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-no LOCAL_CPP_FEATURES := exceptions -LOCAL_STATIC_LIBRARIES := libjsireact libjsi libhermes-executor-common-release -LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes +LOCAL_STATIC_LIBRARIES := libjsireact libhermes-executor-common-release +LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes libjsi include $(BUILD_SHARED_LIBRARY) @@ -34,7 +34,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-no LOCAL_CPP_FEATURES := exceptions -LOCAL_STATIC_LIBRARIES := libjsireact libjsi libhermes-executor-common-debug libhermes-inspector -LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes +LOCAL_STATIC_LIBRARIES := libjsireact libhermes-executor-common-debug libhermes-inspector +LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes libjsi include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/BUCK b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/BUCK index 5f42f833dfb30..fca40c1027f76 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/BUCK @@ -6,6 +6,7 @@ rn_android_library( "HermesExecutor.java", "HermesExecutorFactory.java", ], + autoglob = False, visibility = [ "PUBLIC", ], @@ -25,6 +26,7 @@ rn_android_library( srcs = [ "RuntimeConfig.java", ], + autoglob = False, visibility = [ "PUBLIC", ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutor.java b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutor.java index 7519f2146faf6..2081216f3f9d4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutor.java +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutor.java @@ -16,14 +16,20 @@ public class HermesExecutor extends JavaScriptExecutor { private static String mode_; static { - // libhermes must be loaded explicitly to invoke its JNI_OnLoad. - SoLoader.loadLibrary("hermes"); - try { - SoLoader.loadLibrary("hermes-executor-debug"); - mode_ = "Debug"; - } catch (UnsatisfiedLinkError e) { - SoLoader.loadLibrary("hermes-executor-release"); - mode_ = "Release"; + loadLibrary(); + } + + public static void loadLibrary() throws UnsatisfiedLinkError { + if (mode_ == null) { + // libhermes must be loaded explicitly to invoke its JNI_OnLoad. + SoLoader.loadLibrary("hermes"); + try { + SoLoader.loadLibrary("hermes-executor-debug"); + mode_ = "Debug"; + } catch (UnsatisfiedLinkError e) { + SoLoader.loadLibrary("hermes-executor-release"); + mode_ = "Release"; + } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutorFactory.java b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutorFactory.java index 976af80b1eeb6..ce09abba96110 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutorFactory.java +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/HermesExecutorFactory.java @@ -17,7 +17,7 @@ public class HermesExecutorFactory implements JavaScriptExecutorFactory { private final RuntimeConfig mConfig; public HermesExecutorFactory() { - this(new RuntimeConfig(1024)); + this(null); } public HermesExecutorFactory(RuntimeConfig config) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/RuntimeConfig.java b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/RuntimeConfig.java index fec7c457bc8b9..a3e91b8cce6ff 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/RuntimeConfig.java +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/RuntimeConfig.java @@ -10,10 +10,4 @@ /** Holds runtime configuration for a Hermes VM instance (master or snapshot). */ public final class RuntimeConfig { public long heapSizeMB; - - RuntimeConfig() {} - - RuntimeConfig(long heapSizeMB) { - this.heapSizeMB = heapSizeMB; - } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/hermes/unicode/BUCK b/android/ReactAndroid/src/main/java/com/facebook/hermes/unicode/BUCK index 8f5636e166283..49f7e87f72be9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/hermes/unicode/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/hermes/unicode/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "unicode", srcs = glob(["**/*.java"]), + autoglob = False, visibility = [ "PUBLIC", ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/perftest/BUCK b/android/ReactAndroid/src/main/java/com/facebook/perftest/BUCK index 974cfdbcd0259..1809442c8eae5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/perftest/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/perftest/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library") rn_android_library( name = "perftest", srcs = glob(["*.java"]), + autoglob = False, visibility = [ "PUBLIC", ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/BUCK b/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/BUCK index 31dc81e24005a..bae5e4f25e3c2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/BUCK @@ -9,13 +9,14 @@ rn_prebuilt_jar( fb_native.remote_file( name = "annotations-binary.jar", - sha1 = "3d015bb821875657ac8e4b808a223aae339defb2", - url = "https://jcenter.bintray.com/com/facebook/yoga/proguard-annotations/1.14.1/proguard-annotations-1.14.1.jar", + sha1 = "fcbbb39052e6490eaaf6a6959c49c3a4fbe87c63", + url = "mvn:com.facebook.yoga:proguard-annotations:jar:1.19.0", ) rn_android_library( name = "annotations", srcs = glob(["*.java"]), + autoglob = False, proguard_config = "proguard_annotations.pro", visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/proguard_annotations.pro b/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/proguard_annotations.pro index f9d4677e299ba..19b04a62a2dff 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/proguard_annotations.pro +++ b/android/ReactAndroid/src/main/java/com/facebook/proguard/annotations/proguard_annotations.pro @@ -6,6 +6,7 @@ # Keep our interfaces so they can be used by other ProGuard rules. # See http://sourceforge.net/p/proguard/bugs/466/ -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip +-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStripAny -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters # Do not strip any method/class that is annotated with @DoNotStrip diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/BUCK index e2a345df18109..a32feeb61c982 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "react", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -12,6 +13,7 @@ rn_android_library( react_native_dep("third-party/android/androidx:fragment"), react_native_dep("third-party/android/androidx:legacy-support-core-ui"), react_native_dep("third-party/android/androidx:legacy-support-core-utils"), + react_native_dep("third-party/android/androidx:autofill"), ], visibility = [ "PUBLIC", @@ -43,9 +45,9 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"), react_native_target("java/com/facebook/react/modules/toast:toast"), react_native_target("java/com/facebook/react/surface:surface"), - react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/views/imagehelper:imagehelper"), react_native_target("java/com/facebook/react/config:config"), + react_native_target("java/com/facebook/react/turbomodule/core:core"), react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"), ], exported_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java b/android/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java index 36fb15b0a52d4..59c5c7759c52c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java @@ -35,6 +35,7 @@ import com.facebook.react.uimanager.UIImplementationProvider; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewManager; +import com.facebook.react.uimanager.ViewManagerResolver; import com.facebook.systrace.Systrace; import java.util.HashMap; import java.util.List; @@ -175,8 +176,8 @@ private UIManagerModule createUIManager(final ReactApplicationContext reactConte Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createUIManagerModule"); try { if (mLazyViewManagersEnabled) { - UIManagerModule.ViewManagerResolver resolver = - new UIManagerModule.ViewManagerResolver() { + ViewManagerResolver resolver = + new ViewManagerResolver() { @Override public @Nullable ViewManager getViewManager(String viewManagerName) { return mReactInstanceManager.createViewManager(viewManagerName); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java index 4504a261cb8b4..d5290d968c75b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java @@ -8,6 +8,7 @@ package com.facebook.react; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.view.KeyEvent; import androidx.annotation.Nullable; @@ -65,6 +66,7 @@ protected void onDestroy() { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); mDelegate.onActivityResult(requestCode, resultCode, data); } @@ -120,6 +122,12 @@ public void onWindowFocusChanged(boolean hasFocus) { mDelegate.onWindowFocusChanged(hasFocus); } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDelegate.onConfigurationChanged(newConfig); + } + protected final ReactNativeHost getReactNativeHost() { return mDelegate.getReactNativeHost(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java index 7083f86bfc9cb..fc9090001ce84 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java @@ -11,6 +11,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.view.KeyEvent; @@ -20,9 +21,9 @@ import com.facebook.react.modules.core.PermissionListener; /** - * Delegate class for {@link ReactActivity} and {@link ReactFragmentActivity}. You can subclass this - * to provide custom implementations for e.g. {@link #getReactNativeHost()}, if your Application - * class doesn't implement {@link ReactApplication}. + * Delegate class for {@link ReactActivity}. You can subclass this to provide custom implementations + * for e.g. {@link #getReactNativeHost()}, if your Application class doesn't implement {@link + * ReactApplication}. */ public class ReactActivityDelegate { @@ -154,6 +155,12 @@ public void onWindowFocusChanged(boolean hasFocus) { } } + public void onConfigurationChanged(Configuration newConfig) { + if (getReactNativeHost().hasInstance()) { + getReactInstanceManager().onConfigurationChanged(getContext(), newConfig); + } + } + @TargetApi(Build.VERSION_CODES.M) public void requestPermissions( String[] permissions, int requestCode, PermissionListener listener) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java index a603b374f20a9..c73590c7d2ab0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactAndroidHWInputDeviceHelper.java @@ -29,10 +29,15 @@ public class ReactAndroidHWInputDeviceHelper { .put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, "playPause") .put(KeyEvent.KEYCODE_MEDIA_REWIND, "rewind") .put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, "fastForward") + .put(KeyEvent.KEYCODE_MEDIA_STOP, "stop") + .put(KeyEvent.KEYCODE_MEDIA_NEXT, "next") + .put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, "previous") .put(KeyEvent.KEYCODE_DPAD_UP, "up") .put(KeyEvent.KEYCODE_DPAD_RIGHT, "right") .put(KeyEvent.KEYCODE_DPAD_DOWN, "down") .put(KeyEvent.KEYCODE_DPAD_LEFT, "left") + .put(KeyEvent.KEYCODE_INFO, "info") + .put(KeyEvent.KEYCODE_MENU, "menu") .build(); /** diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java index 5f6f0496ff955..5a4b9c8b49fe4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java @@ -27,8 +27,8 @@ */ public class ReactFragment extends Fragment implements PermissionAwareActivity { - private static final String ARG_COMPONENT_NAME = "arg_component_name"; - private static final String ARG_LAUNCH_OPTIONS = "arg_launch_options"; + protected static final String ARG_COMPONENT_NAME = "arg_component_name"; + protected static final String ARG_LAUNCH_OPTIONS = "arg_launch_options"; private ReactDelegate mReactDelegate; @@ -79,6 +79,10 @@ protected ReactNativeHost getReactNativeHost() { return ((ReactApplication) getActivity().getApplication()).getReactNativeHost(); } + protected ReactDelegate getReactDelegate() { + return mReactDelegate; + } + @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragmentActivity.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragmentActivity.java deleted file mode 100644 index 9a0e1a1c1fea4..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactFragmentActivity.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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; - -/** - * @deprecated ReactFragmentActivity will be removed in 0.59 release. Use {@link ReactActivity} - * instead. - */ -@Deprecated -public abstract class ReactFragmentActivity extends ReactActivity {} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index d685867afbbf7..63b237f25f570 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -41,6 +41,7 @@ import android.os.Bundle; import android.os.Process; import android.view.View; +import android.view.ViewGroup; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import com.facebook.common.logging.FLog; @@ -53,7 +54,6 @@ import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.CatalystInstanceImpl; import com.facebook.react.bridge.JSBundleLoader; -import com.facebook.react.bridge.JSIModule; import com.facebook.react.bridge.JSIModulePackage; import com.facebook.react.bridge.JSIModuleType; import com.facebook.react.bridge.JavaJSExecutor; @@ -65,20 +65,22 @@ import com.facebook.react.bridge.ProxyJavaScriptExecutor; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReactCxxErrorHandler; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; import com.facebook.react.common.LifecycleState; import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.devsupport.DevSupportManagerFactory; -import com.facebook.react.devsupport.ReactInstanceManagerDevHelper; +import com.facebook.react.devsupport.ReactInstanceDevHelper; import com.facebook.react.devsupport.RedBoxHandler; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevSupportManager; @@ -92,7 +94,11 @@ import com.facebook.react.modules.fabric.ReactFabric; import com.facebook.react.packagerconnection.RequestHandler; import com.facebook.react.surface.ReactStage; +import com.facebook.react.turbomodule.core.TurboModuleManager; +import com.facebook.react.turbomodule.core.TurboModuleManagerDelegate; import com.facebook.react.turbomodule.core.interfaces.TurboModuleRegistry; +import com.facebook.react.uimanager.ComponentNameResolver; +import com.facebook.react.uimanager.ComponentNameResolverManager; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.ReactRoot; import com.facebook.react.uimanager.UIImplementationProvider; @@ -102,6 +108,7 @@ import com.facebook.soloader.SoLoader; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -151,11 +158,15 @@ public interface ReactInstanceEventListener { /* accessed from any thread */ private final JavaScriptExecutorFactory mJavaScriptExecutorFactory; + // See {@code ReactInstanceManagerBuilder} for description of all flags here. + private @Nullable List mViewManagerNames = null; private final @Nullable JSBundleLoader mBundleLoader; private final @Nullable String mJSMainModulePath; /* path to JS bundle root on Metro */ private final List mPackages; private final DevSupportManager mDevSupportManager; private final boolean mUseDeveloperSupport; + private final boolean mRequireActivity; + private @Nullable ComponentNameResolverManager mComponentNameResolverManager; private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener; private final Object mReactContextLock = new Object(); private @Nullable volatile ReactContext mCurrentReactContext; @@ -172,7 +183,9 @@ public interface ReactInstanceEventListener { private final MemoryPressureRouter mMemoryPressureRouter; private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; private final @Nullable JSIModulePackage mJSIModulePackage; + private final @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; private List mViewManagers; + private boolean mUseFallbackBundle = false; private class ReactContextInitParams { private final JavaScriptExecutorFactory mJsExecutorFactory; @@ -207,6 +220,8 @@ public static ReactInstanceManagerBuilder builder() { @Nullable String jsMainModulePath, List packages, boolean useDeveloperSupport, + DevSupportManagerFactory devSupportManagerFactory, + boolean requireActivity, @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener, LifecycleState initialLifecycleState, @Nullable UIImplementationProvider mUIImplementationProvider, @@ -217,12 +232,15 @@ public static ReactInstanceManagerBuilder builder() { int minNumShakes, int minTimeLeftInFrameForNonBatchedOperationMs, @Nullable JSIModulePackage jsiModulePackage, - @Nullable Map customPackagerCommandHandlers) { + @Nullable Map customPackagerCommandHandlers, + @Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { FLog.d(TAG, "ReactInstanceManager.ctor()"); initializeSoLoaderIfNecessary(applicationContext); DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext); + // See {@code ReactInstanceManagerBuilder} for description of all flags here. mApplicationContext = applicationContext; mCurrentActivity = currentActivity; mDefaultBackButtonImpl = defaultHardwareBackBtnHandler; @@ -231,10 +249,11 @@ public static ReactInstanceManagerBuilder builder() { mJSMainModulePath = jsMainModulePath; mPackages = new ArrayList<>(); mUseDeveloperSupport = useDeveloperSupport; + mRequireActivity = requireActivity; Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.initDevSupportManager"); mDevSupportManager = - DevSupportManagerFactory.create( + devSupportManagerFactory.create( applicationContext, createDevHelperInterface(), mJSMainModulePath, @@ -242,12 +261,14 @@ public static ReactInstanceManagerBuilder builder() { redBoxHandler, devBundleDownloadListener, minNumShakes, - customPackagerCommandHandlers); + customPackagerCommandHandlers, + surfaceDelegateFactory); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); mBridgeIdleDebugListener = bridgeIdleDebugListener; mLifecycleState = initialLifecycleState; mMemoryPressureRouter = new MemoryPressureRouter(applicationContext); mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; + mTMMDelegateBuilder = tmmDelegateBuilder; synchronized (mPackages) { PrinterHolder.getPrinter() .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Use Split Packages"); @@ -275,10 +296,12 @@ public void invokeDefaultOnBackPressed() { if (mUseDeveloperSupport) { mDevSupportManager.startInspector(); } + + registerCxxErrorHandlerFunc(); } - private ReactInstanceManagerDevHelper createDevHelperInterface() { - return new ReactInstanceManagerDevHelper() { + private ReactInstanceDevHelper createDevHelperInterface() { + return new ReactInstanceDevHelper() { @Override public void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) { ReactInstanceManager.this.onReloadWithJSDebugger(jsExecutorFactory); @@ -309,9 +332,8 @@ public JavaScriptExecutorFactory getJavaScriptExecutorFactory() { Activity currentActivity = getCurrentActivity(); if (currentActivity != null) { ReactRootView rootView = new ReactRootView(currentActivity); - + rootView.setIsFabric(ReactFeatureFlags.enableFabricInLogBox); rootView.startReactApplication(ReactInstanceManager.this, appKey, null); - return rootView; } @@ -333,6 +355,10 @@ public void destroyRootView(View rootView) { }; } + public synchronized void setUseFallbackBundle(boolean useFallbackBundle) { + mUseFallbackBundle = useFallbackBundle; + } + private JavaScriptExecutorFactory getJSExecutorFactory() { return mJavaScriptExecutorFactory; } @@ -349,6 +375,22 @@ public List getPackages() { return new ArrayList<>(mPackages); } + public void handleCxxError(Exception e) { + mDevSupportManager.handleException(e); + } + + public void registerCxxErrorHandlerFunc() { + Class[] parameterTypes = new Class[1]; + parameterTypes[0] = Exception.class; + Method handleCxxErrorFunc = null; + try { + handleCxxErrorFunc = ReactInstanceManager.class.getMethod("handleCxxError", parameterTypes); + } catch (NoSuchMethodException e) { + FLog.e("ReactInstanceHolder", "Failed to set cxx error hanlder function", e); + } + ReactCxxErrorHandler.setHandleErrorFunc(this, handleCxxErrorFunc); + } + static void initializeSoLoaderIfNecessary(Context applicationContext) { // Call SoLoader.initialize here, this is required for apps that does not use exopackage and // does not use SoLoader for loading other native code except from the one used by React Native @@ -419,7 +461,8 @@ public void run() { if (packagerIsRunning) { mDevSupportManager.handleReloadJS(); } else if (mDevSupportManager.hasUpToDateJSBundleInCache() - && !devSettings.isRemoteJSDebugEnabled()) { + && !devSettings.isRemoteJSDebugEnabled() + && !mUseFallbackBundle) { // If there is a up-to-date bundle downloaded from server, // with remote JS debugging disabled, always use that. onJSBundleLoadedFromServer(); @@ -509,12 +552,12 @@ public void onNewIntent(Intent intent) { private void toggleElementInspector() { ReactContext currentContext = getCurrentReactContext(); - if (currentContext != null && currentContext.hasActiveCatalystInstance()) { + if (currentContext != null && currentContext.hasActiveReactInstance()) { currentContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("toggleElementInspector", null); } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new ReactNoCrashSoftException( "Cannot toggleElementInspector, CatalystInstance not available")); @@ -548,16 +591,20 @@ public void onHostPause() { * @param activity the activity being paused */ @ThreadConfined(UI) - public void onHostPause(Activity activity) { - Assertions.assertNotNull(mCurrentActivity); - Assertions.assertCondition( - activity == mCurrentActivity, - "Pausing an activity that is not the current activity, this is incorrect! " - + "Current activity: " - + mCurrentActivity.getClass().getSimpleName() - + " " - + "Paused activity: " - + activity.getClass().getSimpleName()); + public void onHostPause(@Nullable Activity activity) { + if (mRequireActivity) { + Assertions.assertCondition(mCurrentActivity != null); + } + if (mCurrentActivity != null) { + Assertions.assertCondition( + activity == mCurrentActivity, + "Pausing an activity that is not the current activity, this is incorrect! " + + "Current activity: " + + mCurrentActivity.getClass().getSimpleName() + + " " + + "Paused activity: " + + activity.getClass().getSimpleName()); + } onHostPause(); } @@ -573,7 +620,8 @@ public void onHostPause(Activity activity) { * this instance of {@link ReactInstanceManager}. */ @ThreadConfined(UI) - public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { + public void onHostResume( + @Nullable Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { UiThreadUtil.assertOnUiThread(); mDefaultBackButtonImpl = defaultBackButtonImpl; @@ -582,40 +630,47 @@ public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaul /** Use this method when the activity resumes. */ @ThreadConfined(UI) - public void onHostResume(Activity activity) { + public void onHostResume(@Nullable Activity activity) { UiThreadUtil.assertOnUiThread(); mCurrentActivity = activity; if (mUseDeveloperSupport) { - // Resume can be called from one of two different states: + // Resume can be called from one of three different states: // a) when activity was paused // b) when activity has just been created + // c) when there is no activity // In case of (a) the activity is attached to window and it is ok to add new views to it or // open dialogs. In case of (b) there is often a slight delay before such a thing happens. // As dev support manager can add views or open dialogs immediately after it gets enabled // (e.g. in the case when JS bundle is being fetched in background) we only want to enable // it once we know for sure the current activity is attached. + // We want to enable the various devsupport tools in case of (c) even without any activity + + if (mCurrentActivity != null) { + // We check if activity is attached to window by checking if decor view is attached + final View decorView = mCurrentActivity.getWindow().getDecorView(); + if (!ViewCompat.isAttachedToWindow(decorView)) { + decorView.addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + // we can drop listener now that we know the view is attached + decorView.removeOnAttachStateChangeListener(this); + mDevSupportManager.setDevSupportEnabled(true); + } - // We check if activity is attached to window by checking if decor view is attached - final View decorView = mCurrentActivity.getWindow().getDecorView(); - if (!ViewCompat.isAttachedToWindow(decorView)) { - decorView.addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - // we can drop listener now that we know the view is attached - decorView.removeOnAttachStateChangeListener(this); - mDevSupportManager.setDevSupportEnabled(true); - } - - @Override - public void onViewDetachedFromWindow(View v) { - // do nothing - } - }); - } else { - // activity is attached to window, we can enable dev support immediately + @Override + public void onViewDetachedFromWindow(View v) { + // do nothing + } + }); + } else { + // activity is attached to window, we can enable dev support immediately + mDevSupportManager.setDevSupportEnabled(true); + } + } else if (!mRequireActivity) { + // there is no activity, but we can enable dev support mDevSupportManager.setDevSupportEnabled(true); } } @@ -649,7 +704,9 @@ public void onHostDestroy() { * @param activity the activity being destroyed */ @ThreadConfined(UI) - public void onHostDestroy(Activity activity) { + public void onHostDestroy(@Nullable Activity activity) { + // In some cases, Activity may (correctly) be null. + // See mRequireActivity flag. if (activity == mCurrentActivity) { onHostDestroy(); } @@ -706,6 +763,10 @@ public void destroy() { synchronized (mHasStartedDestroying) { mHasStartedDestroying.notifyAll(); } + synchronized (mPackages) { + mViewManagerNames = null; + } + mComponentNameResolverManager = null; FLog.d(ReactConstants.TAG, "ReactInstanceManager has been destroyed"); } @@ -756,7 +817,8 @@ private synchronized void moveReactContextToCurrentLifecycleState() { } @ThreadConfined(UI) - public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + public void onActivityResult( + Activity activity, int requestCode, int resultCode, @Nullable Intent data) { ReactContext currentContext = getCurrentReactContext(); if (currentContext != null) { currentContext.onActivityResult(activity, requestCode, resultCode, data); @@ -794,9 +856,13 @@ public void showDevOptionsDialog() { mDevSupportManager.showDevOptionsDialog(); } + @ThreadConfined(UI) private void clearReactRoot(ReactRoot reactRoot) { - reactRoot.getRootViewGroup().removeAllViews(); - reactRoot.getRootViewGroup().setId(View.NO_ID); + UiThreadUtil.assertOnUiThread(); + reactRoot.getState().compareAndSet(ReactRoot.STATE_STARTED, ReactRoot.STATE_STOPPED); + ViewGroup rootViewGroup = reactRoot.getRootViewGroup(); + rootViewGroup.removeAllViews(); + rootViewGroup.setId(View.NO_ID); } /** @@ -810,17 +876,22 @@ private void clearReactRoot(ReactRoot reactRoot) { @ThreadConfined(UI) public void attachRootView(ReactRoot reactRoot) { UiThreadUtil.assertOnUiThread(); - mAttachedReactRoots.add(reactRoot); - // Reset reactRoot content as it's going to be populated by the application content from JS. - clearReactRoot(reactRoot); + // Calling clearReactRoot is necessary to initialize the Id on reactRoot + // This is necessary independently if the RN Bridge has been initialized or not. + // Ideally reactRoot should be initialized with id == NO_ID + if (mAttachedReactRoots.add(reactRoot)) { + clearReactRoot(reactRoot); + } // If react context is being created in the background, JS application will be started // automatically when creation completes, as reactRoot reactRoot is part of the attached // reactRoot reactRoot list. ReactContext currentContext = getCurrentReactContext(); if (mCreateReactContextThread == null && currentContext != null) { - attachRootViewToInstance(reactRoot); + if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) { + attachRootViewToInstance(reactRoot); + } } } @@ -836,7 +907,7 @@ public void detachRootView(ReactRoot reactRoot) { if (mAttachedReactRoots.contains(reactRoot)) { ReactContext currentContext = getCurrentReactContext(); mAttachedReactRoots.remove(reactRoot); - if (currentContext != null && currentContext.hasActiveCatalystInstance()) { + if (currentContext != null && currentContext.hasActiveReactInstance()) { detachViewFromInstance(reactRoot, currentContext.getCatalystInstance()); } } @@ -871,7 +942,7 @@ public List getOrCreateViewManagers( ReactApplicationContext context; synchronized (mReactContextLock) { context = (ReactApplicationContext) getCurrentReactContext(); - if (context == null || !context.hasActiveCatalystInstance()) { + if (context == null || !context.hasActiveReactInstance()) { return null; } } @@ -893,32 +964,39 @@ public List getOrCreateViewManagers( public @Nullable List getViewManagerNames() { Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.getViewManagerNames"); + List viewManagerNames = mViewManagerNames; + if (viewManagerNames != null) { + return viewManagerNames; + } ReactApplicationContext context; synchronized (mReactContextLock) { context = (ReactApplicationContext) getCurrentReactContext(); - if (context == null || !context.hasActiveCatalystInstance()) { + if (context == null || !context.hasActiveReactInstance()) { return null; } } synchronized (mPackages) { - Set uniqueNames = new HashSet<>(); - for (ReactPackage reactPackage : mPackages) { - SystraceMessage.beginSection( - TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.getViewManagerName") - .arg("Package", reactPackage.getClass().getSimpleName()) - .flush(); - if (reactPackage instanceof ViewManagerOnDemandReactPackage) { - List names = - ((ViewManagerOnDemandReactPackage) reactPackage).getViewManagerNames(context); - if (names != null) { - uniqueNames.addAll(names); + if (mViewManagerNames == null) { + Set uniqueNames = new HashSet<>(); + for (ReactPackage reactPackage : mPackages) { + SystraceMessage.beginSection( + TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.getViewManagerName") + .arg("Package", reactPackage.getClass().getSimpleName()) + .flush(); + if (reactPackage instanceof ViewManagerOnDemandReactPackage) { + List names = + ((ViewManagerOnDemandReactPackage) reactPackage).getViewManagerNames(context); + if (names != null) { + uniqueNames.addAll(names); + } } + SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush(); } - SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush(); + Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); + mViewManagerNames = new ArrayList<>(uniqueNames); } - Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); - return new ArrayList<>(uniqueNames); + return mViewManagerNames; } } @@ -987,6 +1065,9 @@ private void recreateReactContextInBackground( private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) { FLog.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()"); UiThreadUtil.assertOnUiThread(); + + // Mark start of bridge loading + ReactMarker.logMarker(ReactMarkerConstants.REACT_BRIDGE_LOADING_START); synchronized (mAttachedReactRoots) { synchronized (mReactContextLock) { if (mCurrentReactContext != null) { @@ -1087,7 +1168,9 @@ private void setupReactContext(final ReactApplicationContext reactContext) { ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START); for (ReactRoot reactRoot : mAttachedReactRoots) { - attachRootViewToInstance(reactRoot); + if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) { + attachRootViewToInstance(reactRoot); + } } ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END); } @@ -1116,6 +1199,8 @@ public void run() { }); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(SETUP_REACT_CONTEXT_END); + // Mark end of bridge loading + ReactMarker.logMarker(ReactMarkerConstants.REACT_BRIDGE_LOADING_END); reactContext.runOnJSQueueThread( new Runnable() { @Override @@ -1144,7 +1229,8 @@ private void attachRootViewToInstance(final ReactRoot reactRoot) { // If we can't get a UIManager something has probably gone horribly wrong if (uiManager == null) { throw new IllegalStateException( - "Unable to attach a rootView to ReactInstance when UIManager is not properly initialized."); + "Unable to attach a rootView to ReactInstance when UIManager is not properly" + + " initialized."); } @Nullable Bundle initialProperties = reactRoot.getAppProperties(); @@ -1203,6 +1289,7 @@ private void detachViewFromInstance(ReactRoot reactRoot, CatalystInstance cataly } } + @ThreadConfined(UI) private void tearDownReactContext(ReactContext reactContext) { FLog.d(ReactConstants.TAG, "ReactInstanceManager.tearDownReactContext()"); UiThreadUtil.assertOnUiThread(); @@ -1260,25 +1347,35 @@ private ReactApplicationContext createReactContext( reactContext.initializeWithInstance(catalystInstance); - if (mJSIModulePackage != null) { - catalystInstance.addJSIModules( - mJSIModulePackage.getJSIModules( - reactContext, catalystInstance.getJavaScriptContextHolder())); + if (ReactFeatureFlags.useTurboModules && mTMMDelegateBuilder != null) { + TurboModuleManagerDelegate tmmDelegate = + mTMMDelegateBuilder + .setPackages(mPackages) + .setReactApplicationContext(reactContext) + .build(); - if (ReactFeatureFlags.useTurboModules) { - JSIModule turboModuleManager = - catalystInstance.getJSIModule(JSIModuleType.TurboModuleManager); + TurboModuleManager turboModuleManager = + new TurboModuleManager( + catalystInstance.getRuntimeExecutor(), + tmmDelegate, + catalystInstance.getJSCallInvokerHolder(), + catalystInstance.getNativeCallInvokerHolder()); - catalystInstance.setTurboModuleManager(turboModuleManager); + catalystInstance.setTurboModuleManager(turboModuleManager); - TurboModuleRegistry registry = (TurboModuleRegistry) turboModuleManager; + TurboModuleRegistry registry = (TurboModuleRegistry) turboModuleManager; - // Eagerly initialize TurboModules - for (String moduleName : registry.getEagerInitModuleNames()) { - registry.getModule(moduleName); - } + // Eagerly initialize TurboModules + for (String moduleName : registry.getEagerInitModuleNames()) { + registry.getModule(moduleName); } } + + if (mJSIModulePackage != null) { + catalystInstance.addJSIModules( + mJSIModulePackage.getJSIModules( + reactContext, catalystInstance.getJavaScriptContextHolder())); + } if (ReactFeatureFlags.eagerInitializeFabric) { catalystInstance.getJSIModule(JSIModuleType.UIManager); } @@ -1288,6 +1385,24 @@ private ReactApplicationContext createReactContext( if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) { catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true"); } + if (ReactFeatureFlags.enableExperimentalStaticViewConfigs) { + mComponentNameResolverManager = + new ComponentNameResolverManager( + catalystInstance.getRuntimeExecutor(), + new ComponentNameResolver() { + @Override + public String[] getComponentNames() { + List viewManagerNames = getViewManagerNames(); + if (viewManagerNames == null) { + FLog.e(TAG, "No ViewManager names found"); + return new String[0]; + } + return viewManagerNames.toArray(new String[0]); + } + }); + catalystInstance.setGlobalVariable("__fbStaticViewConfig", "true"); + } + ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle"); catalystInstance.runJSBundle(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index a3b33f6cfa3b7..672d370cd0632 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -14,6 +14,7 @@ import android.app.Application; import android.content.Context; import androidx.annotation.Nullable; +import com.facebook.hermes.reactexecutor.HermesExecutor; import com.facebook.hermes.reactexecutor.HermesExecutorFactory; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.JSBundleLoader; @@ -22,9 +23,13 @@ import com.facebook.react.bridge.NativeModuleCallExceptionHandler; import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener; import com.facebook.react.common.LifecycleState; +import com.facebook.react.common.SurfaceDelegateFactory; +import com.facebook.react.devsupport.DefaultDevSupportManagerFactory; +import com.facebook.react.devsupport.DevSupportManagerFactory; import com.facebook.react.devsupport.RedBoxHandler; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevSupportManager; +import com.facebook.react.jscexecutor.JSCExecutor; import com.facebook.react.jscexecutor.JSCExecutorFactory; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.packagerconnection.RequestHandler; @@ -45,6 +50,8 @@ public class ReactInstanceManagerBuilder { private @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener; private @Nullable Application mApplication; private boolean mUseDeveloperSupport; + private @Nullable DevSupportManagerFactory mDevSupportManagerFactory; + private boolean mRequireActivity; private @Nullable LifecycleState mInitialLifecycleState; private @Nullable UIImplementationProvider mUIImplementationProvider; private @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; @@ -58,6 +65,8 @@ public class ReactInstanceManagerBuilder { private int mMinTimeLeftInFrameForNonBatchedOperationMs = -1; private @Nullable JSIModulePackage mJSIModulesPackage; private @Nullable Map mCustomPackagerCommandHandlers; + private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; + private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; /* package protected */ ReactInstanceManagerBuilder() {} @@ -170,6 +179,40 @@ public ReactInstanceManagerBuilder setUseDeveloperSupport(boolean useDeveloperSu return this; } + /** + * Set the custom {@link DevSupportManagerFactory}. If not set, will use {@link + * DefaultDevSupportManagerFactory}. + */ + public ReactInstanceManagerBuilder setDevSupportManagerFactory( + final DevSupportManagerFactory devSupportManagerFactory) { + mDevSupportManagerFactory = devSupportManagerFactory; + return this; + } + + /** + * When {@code false}, indicates that correct usage of React Native will NOT involve an Activity. + * For the vast majority of Android apps in the ecosystem, this will not need to change. Unless + * you really know what you're doing, you should probably not change this! + */ + public ReactInstanceManagerBuilder setRequireActivity(boolean requireActivity) { + mRequireActivity = requireActivity; + return this; + } + + /** + * When the {@link SurfaceDelegateFactory} is provided, it will be used for native modules to get + * a {@link SurfaceDelegate} to interact with the platform specific surface that they that needs + * to be rendered in. For mobile platform this is default to be null so that these modules will + * need to provide a default surface delegate. One example of such native module is LogBoxModule, + * which is rendered in mobile platform with LogBoxDialog, while in VR platform with custom layer + * provided by runtime. + */ + public ReactInstanceManagerBuilder setSurfaceDelegateFactory( + @Nullable final SurfaceDelegateFactory surfaceDelegateFactory) { + mSurfaceDelegateFactory = surfaceDelegateFactory; + return this; + } + /** * Sets the initial lifecycle state of the host. For example, if the host is already resumed at * creation time, we wouldn't expect an onResume call until we get an onPause call. @@ -224,6 +267,12 @@ public ReactInstanceManagerBuilder setCustomPackagerCommandHandlers( return this; } + public ReactInstanceManagerBuilder setReactPackageTurboModuleManagerDelegateBuilder( + @Nullable ReactPackageTurboModuleManagerDelegate.Builder builder) { + mTMMDelegateBuilder = builder; + return this; + } + /** * Instantiates a new {@link ReactInstanceManager}. Before calling {@code build}, the following * must be called: @@ -276,6 +325,10 @@ public ReactInstanceManager build() { mJSMainModulePath, mPackages, mUseDeveloperSupport, + mDevSupportManagerFactory == null + ? new DefaultDevSupportManagerFactory() + : mDevSupportManagerFactory, + mRequireActivity, mBridgeIdleDebugListener, Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"), mUIImplementationProvider, @@ -286,7 +339,9 @@ public ReactInstanceManager build() { mMinNumShakes, mMinTimeLeftInFrameForNonBatchedOperationMs, mJSIModulesPackage, - mCustomPackagerCommandHandlers); + mCustomPackagerCommandHandlers, + mTMMDelegateBuilder, + mSurfaceDelegateFactory); } private JavaScriptExecutorFactory getDefaultJSExecutorFactory( @@ -294,7 +349,7 @@ private JavaScriptExecutorFactory getDefaultJSExecutorFactory( try { // If JSC is included, use it as normal initializeSoLoaderIfNecessary(applicationContext); - SoLoader.loadLibrary("jscexecutor"); + JSCExecutor.loadLibrary(); return new JSCExecutorFactory(appName, deviceName); } catch (UnsatisfiedLinkError jscE) { // https://github.com/facebook/hermes/issues/78 shows that @@ -312,6 +367,7 @@ private JavaScriptExecutorFactory getDefaultJSExecutorFactory( // Otherwise use Hermes try { + HermesExecutor.loadLibrary(); return new HermesExecutorFactory(); } catch (UnsatisfiedLinkError hermesE) { // If we get here, either this is a JSC build, and of course diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java index 188ae05f361ee..5ee746a4f6cf5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java @@ -15,6 +15,9 @@ import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; import com.facebook.react.common.LifecycleState; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.common.SurfaceDelegateFactory; +import com.facebook.react.devsupport.DevSupportManagerFactory; import com.facebook.react.devsupport.RedBoxHandler; import com.facebook.react.uimanager.UIImplementationProvider; import java.util.List; @@ -68,11 +71,16 @@ protected ReactInstanceManager createReactInstanceManager() { .setApplication(mApplication) .setJSMainModulePath(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) + .setDevSupportManagerFactory(getDevSupportManagerFactory()) + .setRequireActivity(getShouldRequireActivity()) + .setSurfaceDelegateFactory(getSurfaceDelegateFactory()) .setRedBoxHandler(getRedBoxHandler()) .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory()) .setUIImplementationProvider(getUIImplementationProvider()) .setJSIModulesPackage(getJSIModulePackage()) - .setInitialLifecycleState(LifecycleState.BEFORE_CREATE); + .setInitialLifecycleState(LifecycleState.BEFORE_CREATE) + .setReactPackageTurboModuleManagerDelegateBuilder( + getReactPackageTurboModuleManagerDelegateBuilder()); for (ReactPackage reactPackage : getPackages()) { builder.addPackage(reactPackage); @@ -99,6 +107,11 @@ protected ReactInstanceManager createReactInstanceManager() { return null; } + protected @Nullable ReactPackageTurboModuleManagerDelegate.Builder + getReactPackageTurboModuleManagerDelegateBuilder() { + return null; + } + protected final Application getApplication() { return mApplication; } @@ -117,6 +130,26 @@ protected UIImplementationProvider getUIImplementationProvider() { return null; } + /** Returns whether or not to treat it as normal if Activity is null. */ + public boolean getShouldRequireActivity() { + return true; + } + + /** + * Return the {@link SurfaceDelegateFactory} used by NativeModules to get access to a {@link + * SurfaceDelegate} to interact with a surface. By default in the mobile platform the {@link + * SurfaceDelegate} it returns is null, and the NativeModule needs to implement its own {@link + * SurfaceDelegate} to decide how it would interact with its own container surface. + */ + public SurfaceDelegateFactory getSurfaceDelegateFactory() { + return new SurfaceDelegateFactory() { + @Override + public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) { + return null; + } + }; + } + /** * Returns the name of the main module. Determines the URL used to fetch the JS bundle from Metro. * It is only used when dev support is enabled. This is the first file to be executed once the @@ -147,6 +180,11 @@ protected String getJSMainModuleName() { /** Returns whether dev mode should be enabled. This enables e.g. the dev menu. */ public abstract boolean getUseDeveloperSupport(); + /** Get the {@link DevSupportManagerFactory}. Override this to use a custom dev support manager */ + protected @Nullable DevSupportManagerFactory getDevSupportManagerFactory() { + return null; + } + /** * Returns a list of {@link ReactPackage} used by the app. You'll most likely want to return at * least the {@code MainReactPackage}. If your app uses additional views or modules besides the diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java similarity index 82% rename from android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java rename to android/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java index 65ee5186e476b..2a9c99549bda5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java @@ -5,23 +5,26 @@ * LICENSE file in the root directory of this source tree. */ -package com.facebook.react.turbomodule.core; +package com.facebook.react; import androidx.annotation.Nullable; import com.facebook.infer.annotation.Assertions; import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.ReactPackage; -import com.facebook.react.TurboReactPackage; import com.facebook.react.bridge.CxxModuleWrapper; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.turbomodule.core.TurboModuleManagerDelegate; import com.facebook.react.turbomodule.core.interfaces.TurboModule; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public abstract class ReactPackageTurboModuleManagerDelegate extends TurboModuleManagerDelegate { private final List mPackages = new ArrayList<>(); + private final Map> mPackageModuleInfos = + new HashMap<>(); private final ReactApplicationContext mReactApplicationContext; protected ReactPackageTurboModuleManagerDelegate( @@ -30,7 +33,9 @@ protected ReactPackageTurboModuleManagerDelegate( mReactApplicationContext = reactApplicationContext; for (ReactPackage reactPackage : packages) { if (reactPackage instanceof TurboReactPackage) { - mPackages.add((TurboReactPackage) reactPackage); + TurboReactPackage pkg = (TurboReactPackage) reactPackage; + mPackages.add(pkg); + mPackageModuleInfos.put(pkg, pkg.getReactModuleInfoProvider().getReactModuleInfos()); } } } @@ -72,8 +77,15 @@ private TurboModule resolveModule(String moduleName) { for (final TurboReactPackage pkg : mPackages) { try { - NativeModule module = pkg.getModule(moduleName, mReactApplicationContext); - if (resolvedModule == null || module != null && module.canOverrideExistingModule()) { + final ReactModuleInfo moduleInfo = mPackageModuleInfos.get(pkg).get(moduleName); + if (moduleInfo == null + || !moduleInfo.isTurboModule() + || resolvedModule != null && !moduleInfo.canOverrideExistingModule()) { + continue; + } + + final NativeModule module = pkg.getModule(moduleName, mReactApplicationContext); + if (module != null) { resolvedModule = module; } } catch (IllegalArgumentException ex) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/android/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index a205467c09428..875c5085a31fe 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -16,14 +16,17 @@ import android.graphics.Canvas; import android.graphics.Point; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.util.AttributeSet; +import android.view.DisplayCutout; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.WindowInsets; import android.view.WindowManager; import android.widget.FrameLayout; import androidx.annotation.Nullable; @@ -35,13 +38,13 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactNoCrashSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.modules.appregistry.AppRegistry; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.deviceinfo.DeviceInfoModule; @@ -50,13 +53,15 @@ import com.facebook.react.uimanager.IllegalViewOperationException; import com.facebook.react.uimanager.JSTouchDispatcher; import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.uimanager.ReactClippingProhibitedView; import com.facebook.react.uimanager.ReactRoot; import com.facebook.react.uimanager.RootView; +import com.facebook.react.uimanager.RootViewUtil; import com.facebook.react.uimanager.UIManagerHelper; -import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.systrace.Systrace; +import java.util.concurrent.atomic.AtomicInteger; /** * Default root view for catalyst apps. Provides the ability to listen for size changes so that a UI @@ -85,7 +90,8 @@ public interface ReactRootViewEventListener { private @Nullable String mInitialUITemplate; private @Nullable CustomGlobalLayoutListener mCustomGlobalLayoutListener; private @Nullable ReactRootViewEventListener mRootViewEventListener; - private int mRootViewTag; + private int mRootViewTag = + 0; /* This should be View.NO_ID, but for legacy reasons we haven't migrated yet */ private boolean mIsAttachedToInstance; private boolean mShouldLogContentAppeared; private @Nullable JSTouchDispatcher mJSTouchDispatcher; @@ -99,6 +105,7 @@ public interface ReactRootViewEventListener { private int mLastOffsetX = Integer.MIN_VALUE; private int mLastOffsetY = Integer.MIN_VALUE; private @UIManagerType int mUIManagerType = DEFAULT; + private final AtomicInteger mState = new AtomicInteger(STATE_STOPPED); public ReactRootView(Context context) { super(context); @@ -122,6 +129,7 @@ private void init() { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactRootView.onMeasure"); + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_ON_MEASURE_START); try { boolean measureSpecsUpdated = widthMeasureSpec != mWidthMeasureSpec || heightMeasureSpec != mHeightMeasureSpec; @@ -171,6 +179,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mLastHeight = height; } finally { + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_ON_MEASURE_END); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } } @@ -188,7 +197,7 @@ public void onChildStartedNativeGesture(MotionEvent androidEvent) { return; } ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); + UIManager uiManager = UIManagerHelper.getUIManager(reactContext, getUIManagerType()); if (uiManager != null) { EventDispatcher eventDispatcher = uiManager.getEventDispatcher(); @@ -276,7 +285,7 @@ private void dispatchJSTouchEvent(MotionEvent event) { return; } ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); + UIManager uiManager = UIManagerHelper.getUIManager(reactContext, getUIManagerType()); if (uiManager != null) { EventDispatcher eventDispatcher = uiManager.getEventDispatcher(); @@ -325,9 +334,29 @@ private void removeOnGlobalLayoutListener() { } @Override - public void onViewAdded(View child) { + public void onViewAdded(final View child) { super.onViewAdded(child); + // See comments in {@code ReactRootViewProhibitedChildView} for why we want this mechanism. + if (child instanceof ReactClippingProhibitedView) { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + if (!child.isShown()) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "A view was illegally added as a child of a ReactRootView. " + + "This View should not be a direct child of a ReactRootView, because it is not visible and will never be reachable. Child: " + + child.getClass().getCanonicalName().toString() + + " child ID: " + + child.getId())); + } + } + }); + } + if (mShouldLogContentAppeared) { mShouldLogContentAppeared = false; @@ -412,18 +441,8 @@ public String getSurfaceID() { return appProperties != null ? appProperties.getString("surfaceID") : null; } - public static Point getViewportOffset(View v) { - int[] locationInWindow = new int[2]; - v.getLocationInWindow(locationInWindow); - - // we need to subtract visibleWindowCoords - to subtract possible window insets, split - // screen or multi window - Rect visibleWindowFrame = new Rect(); - v.getWindowVisibleDisplayFrame(visibleWindowFrame); - locationInWindow[0] -= visibleWindowFrame.left; - locationInWindow[1] -= visibleWindowFrame.top; - - return new Point(locationInWindow[0], locationInWindow[1]); + public AtomicInteger getState() { + return mState; } /** @@ -437,10 +456,20 @@ public static Point getViewportOffset(View v) { */ private void updateRootLayoutSpecs( boolean measureSpecsChanged, final int widthMeasureSpec, final int heightMeasureSpec) { + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_UPDATE_LAYOUT_SPECS_START); if (mReactInstanceManager == null) { + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_UPDATE_LAYOUT_SPECS_END); FLog.w(TAG, "Unable to update root layout specs for uninitialized ReactInstanceManager"); return; } + // In Fabric we cannot call `updateRootLayoutSpecs` until a SurfaceId has been set. + // Sometimes, + if (getUIManagerType() == FABRIC && !isRootViewTagSet()) { + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_UPDATE_LAYOUT_SPECS_END); + FLog.e(TAG, "Unable to update root layout specs for ReactRootView: no rootViewTag set yet"); + return; + } + final ReactContext reactApplicationContext = mReactInstanceManager.getCurrentReactContext(); if (reactApplicationContext != null) { @@ -453,7 +482,7 @@ private void updateRootLayoutSpecs( int offsetX = 0; int offsetY = 0; if (getUIManagerType() == FABRIC) { - Point viewportOffset = getViewportOffset(this); + Point viewportOffset = RootViewUtil.getViewportOffset(this); offsetX = viewportOffset.x; offsetY = viewportOffset.y; } @@ -466,6 +495,8 @@ private void updateRootLayoutSpecs( mLastOffsetY = offsetY; } } + + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_UPDATE_LAYOUT_SPECS_END); } /** @@ -483,25 +514,30 @@ public void unmountReactApplication() { // to be committed via the Scheduler, which will cause mounting instructions // to be queued up and synchronously executed to delete and remove // all the views in the hierarchy. - if (mReactInstanceManager != null && ReactFeatureFlags.enableStopSurfaceOnRootViewUnmount) { + if (mReactInstanceManager != null) { final ReactContext reactApplicationContext = mReactInstanceManager.getCurrentReactContext(); if (reactApplicationContext != null && getUIManagerType() == FABRIC) { @Nullable UIManager uiManager = UIManagerHelper.getUIManager(reactApplicationContext, getUIManagerType()); if (uiManager != null) { - // TODO T48186892: remove when resolved - FLog.e( - TAG, - "stopSurface for surfaceId: " + this.getId(), - new RuntimeException("unmountReactApplication")); - if (getId() == NO_ID) { - ReactSoftException.logSoftException( + final int surfaceId = this.getId(); + + // In case of "retry" or error dialogues being shown, this ReactRootView could be + // reused (with the same surfaceId, or a different one). Ensure the ReactRootView + // is marked as unused in case of that. + setId(NO_ID); + + // Remove all children from ReactRootView + removeAllViews(); + + if (surfaceId == NO_ID) { + ReactSoftExceptionLogger.logSoftException( TAG, new RuntimeException( "unmountReactApplication called on ReactRootView with invalid id")); } else { - uiManager.stopSurface(this.getId()); + uiManager.stopSurface(surfaceId); } } } @@ -537,7 +573,7 @@ public void onAttachedToReactInstance() { } } - public void setEventListener(ReactRootViewEventListener eventListener) { + public void setEventListener(@Nullable ReactRootViewEventListener eventListener) { mRootViewEventListener = eventListener; } @@ -560,7 +596,7 @@ public String getJSModuleName() { public void setAppProperties(@Nullable Bundle appProperties) { UiThreadUtil.assertOnUiThread(); mAppProperties = appProperties; - if (getRootViewTag() != 0) { + if (isRootViewTagSet()) { runApplication(); } } @@ -614,6 +650,11 @@ public void runApplication() { mJSTouchDispatcher = new JSTouchDispatcher(this); } + @VisibleForTesting + /* package */ void simulateCheckForKeyboardForTesting() { + getCustomGlobalLayoutListener().checkForKeyboardEvents(); + } + private CustomGlobalLayoutListener getCustomGlobalLayoutListener() { if (mCustomGlobalLayoutListener == null) { mCustomGlobalLayoutListener = new CustomGlobalLayoutListener(); @@ -623,16 +664,28 @@ private CustomGlobalLayoutListener getCustomGlobalLayoutListener() { private void attachToReactInstanceManager() { Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachToReactInstanceManager"); + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_ATTACH_TO_REACT_INSTANCE_MANAGER_START); - if (mIsAttachedToInstance) { - return; + // React Native requires that the RootView id be managed entirely by React Native, and will + // crash in addRootView/startSurface if the native View id isn't set to NO_ID. + if (getId() != View.NO_ID) { + throw new IllegalViewOperationException( + "Trying to attach a ReactRootView with an explicit id already set to [" + + getId() + + "]. React Native uses the id field to track react tags and will overwrite this" + + " field. If that is fine, explicitly overwrite the id field to View.NO_ID."); } try { + if (mIsAttachedToInstance) { + return; + } + mIsAttachedToInstance = true; Assertions.assertNotNull(mReactInstanceManager).attachRootView(this); getViewTreeObserver().addOnGlobalLayoutListener(getCustomGlobalLayoutListener()); } finally { + ReactMarker.logMarker(ReactMarkerConstants.ROOT_VIEW_ATTACH_TO_REACT_INSTANCE_MANAGER_END); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } } @@ -653,6 +706,10 @@ public int getRootViewTag() { return mRootViewTag; } + private boolean isRootViewTagSet() { + return mRootViewTag != 0 && mRootViewTag != NO_ID; + } + public void setRootViewTag(int rootViewTag) { mRootViewTag = rootViewTag; } @@ -717,8 +774,20 @@ public void onGlobalLayout() { private void checkForKeyboardEvents() { getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea); + int notchHeight = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + WindowInsets insets = getRootView().getRootWindowInsets(); + if (insets != null) { + DisplayCutout displayCutout = insets.getDisplayCutout(); + if (displayCutout != null) { + notchHeight = displayCutout.getSafeInsetTop(); + } + } + } final int heightDiff = - DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels - mVisibleViewArea.bottom; + DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels + - mVisibleViewArea.bottom + + notchHeight; boolean isKeyboardShowingOrKeyboardHeightChanged = mKeyboardHeight != heightDiff && heightDiff > mMinKeyboardHeightDetected; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK index 9c1b8a17ecc67..1f4e245b01f88 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK @@ -5,6 +5,7 @@ rn_android_library( srcs = glob([ "*.java", ]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -28,5 +29,5 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index 2f2880080e6fc..0c4badcd481d9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -17,7 +17,7 @@ import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UIManagerListener; @@ -365,7 +365,7 @@ private void initializeLifecycleEventListenersForViewTag(final int viewTag) { if (nodesManager != null) { nodesManager.initializeEventListenerForUIManagerType(mUIManagerType); } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( NAME, new RuntimeException( "initializeLifecycleEventListenersForViewTag could not get NativeAnimatedNodesManager")); @@ -891,4 +891,12 @@ public void execute(NativeAnimatedNodesManager animatedNodesManager) { } }); } + + @Override + public void invalidate() { + ReactApplicationContext context = getReactApplicationContextIfActiveOrWarn(); + if (context != null) { + context.removeLifecycleEventListener(this); + } + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 9bf6d02fd2a11..a2707f5842ed6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -17,7 +17,7 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.UIManager; @@ -121,7 +121,7 @@ public boolean hasActiveAnimations() { public void createAnimatedNode(int tag, ReadableMap config) { if (mAnimatedNodes.get(tag) != null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " already exists"); + "createAnimatedNode: Animated node [" + tag + "] already exists"); } String type = config.getString("type"); final AnimatedNode node; @@ -168,7 +168,9 @@ public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exist, or is not a 'value' node"); + "startListeningToAnimatedNodeValue: Animated node [" + + tag + + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).setValueListener(listener); } @@ -178,7 +180,9 @@ public void stopListeningToAnimatedNodeValue(int tag) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exist, or is not a 'value' node"); + "startListeningToAnimatedNodeValue: Animated node [" + + tag + + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).setValueListener(null); } @@ -188,7 +192,9 @@ public void setAnimatedNodeValue(int tag, double value) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exist, or is not a 'value' node"); + "setAnimatedNodeValue: Animated node [" + + tag + + "] does not exist, or is not a 'value' node"); } stopAnimationsForNode(node); ((ValueAnimatedNode) node).mValue = value; @@ -200,7 +206,9 @@ public void setAnimatedNodeOffset(int tag, double offset) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exist, or is not a 'value' node"); + "setAnimatedNodeOffset: Animated node [" + + tag + + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).mOffset = offset; mUpdatedNodes.put(tag, node); @@ -211,7 +219,9 @@ public void flattenAnimatedNodeOffset(int tag) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exist, or is not a 'value' node"); + "flattenAnimatedNodeOffset: Animated node [" + + tag + + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).flattenOffset(); } @@ -221,7 +231,9 @@ public void extractAnimatedNodeOffset(int tag) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exist, or is not a 'value' node"); + "extractAnimatedNodeOffset: Animated node [" + + tag + + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).extractOffset(); } @@ -232,11 +244,14 @@ public void startAnimatingNode( AnimatedNode node = mAnimatedNodes.get(animatedNodeTag); if (node == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + animatedNodeTag + " does not exist"); + "startAnimatingNode: Animated node [" + animatedNodeTag + "] does not exist"); } if (!(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node should be of type " + ValueAnimatedNode.class.getName()); + "startAnimatingNode: Animated node [" + + animatedNodeTag + + "] should be of type " + + ValueAnimatedNode.class.getName()); } final AnimationDriver existingDriver = mActiveAnimations.get(animationId); @@ -256,7 +271,8 @@ public void startAnimatingNode( } else if ("decay".equals(type)) { animation = new DecayAnimation(animationConfig); } else { - throw new JSApplicationIllegalArgumentException("Unsupported animation type: " + type); + throw new JSApplicationIllegalArgumentException( + "startAnimatingNode: Unsupported animation type [" + animatedNodeTag + "]: " + type); } animation.mId = animationId; animation.mEndCallback = endCallback; @@ -315,12 +331,16 @@ public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) { AnimatedNode parentNode = mAnimatedNodes.get(parentNodeTag); if (parentNode == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + parentNodeTag + " does not exists"); + "connectAnimatedNodes: Animated node with tag (parent) [" + + parentNodeTag + + "] does not exist"); } AnimatedNode childNode = mAnimatedNodes.get(childNodeTag); if (childNode == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + childNodeTag + " does not exists"); + "connectAnimatedNodes: Animated node with tag (child) [" + + childNodeTag + + "] does not exist"); } parentNode.addChild(childNode); mUpdatedNodes.put(childNodeTag, childNode); @@ -330,12 +350,16 @@ public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) { AnimatedNode parentNode = mAnimatedNodes.get(parentNodeTag); if (parentNode == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + parentNodeTag + " does not exists"); + "disconnectAnimatedNodes: Animated node with tag (parent) [" + + parentNodeTag + + "] does not exist"); } AnimatedNode childNode = mAnimatedNodes.get(childNodeTag); if (childNode == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + childNodeTag + " does not exists"); + "disconnectAnimatedNodes: Animated node with tag (child) [" + + childNodeTag + + "] does not exist"); } parentNode.removeChild(childNode); mUpdatedNodes.put(childNodeTag, childNode); @@ -346,27 +370,31 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) { AnimatedNode node = mAnimatedNodes.get(animatedNodeTag); if (node == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + animatedNodeTag + " does not exists"); + "connectAnimatedNodeToView: Animated node with tag [" + + animatedNodeTag + + "] does not exist"); } if (!(node instanceof PropsAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node connected to view should be" - + "of type " + "connectAnimatedNodeToView: Animated node connected to view [" + + viewTag + + "] should be of type " + PropsAnimatedNode.class.getName()); } if (mReactApplicationContext == null) { throw new IllegalStateException( - "Animated node could not be connected, no ReactApplicationContext: " + viewTag); + "connectAnimatedNodeToView: Animated node could not be connected, no ReactApplicationContext: " + + viewTag); } @Nullable UIManager uiManager = UIManagerHelper.getUIManagerForReactTag(mReactApplicationContext, viewTag); if (uiManager == null) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new ReactNoCrashSoftException( - "Animated node could not be connected to UIManager - uiManager disappeared for tag: " + "connectAnimatedNodeToView: Animated node could not be connected to UIManager - uiManager disappeared for tag: " + viewTag)); return; } @@ -381,12 +409,15 @@ public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) { AnimatedNode node = mAnimatedNodes.get(animatedNodeTag); if (node == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + animatedNodeTag + " does not exists"); + "disconnectAnimatedNodeFromView: Animated node with tag [" + + animatedNodeTag + + "] does not exist"); } if (!(node instanceof PropsAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node connected to view should be" - + "of type " + "disconnectAnimatedNodeFromView: Animated node connected to view [" + + viewTag + + "] should be of type " + PropsAnimatedNode.class.getName()); } PropsAnimatedNode propsAnimatedNode = (PropsAnimatedNode) node; @@ -398,7 +429,7 @@ public void getValue(int tag, Callback callback) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + tag + " does not exists or is not a 'value' node"); + "getValue: Animated node with tag [" + tag + "] does not exist or is not a 'value' node"); } callback.invoke(((ValueAnimatedNode) node).getValue()); } @@ -415,8 +446,7 @@ public void restoreDefaultValues(int animatedNodeTag) { } if (!(node instanceof PropsAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node connected to view should be" - + "of type " + "Animated node connected to view [?] should be of type " + PropsAnimatedNode.class.getName()); } PropsAnimatedNode propsAnimatedNode = (PropsAnimatedNode) node; @@ -429,12 +459,15 @@ public void addAnimatedEventToView(int viewTag, String eventName, ReadableMap ev AnimatedNode node = mAnimatedNodes.get(nodeTag); if (node == null) { throw new JSApplicationIllegalArgumentException( - "Animated node with tag " + nodeTag + " does not exists"); + "addAnimatedEventToView: Animated node with tag [" + nodeTag + "] does not exist"); } if (!(node instanceof ValueAnimatedNode)) { throw new JSApplicationIllegalArgumentException( - "Animated node connected to event should be" - + "of type " + "addAnimatedEventToView: Animated node on view [" + + viewTag + + "] connected to event (" + + eventName + + ") should be of type " + ValueAnimatedNode.class.getName()); } @@ -502,7 +535,7 @@ private void handleEvent(Event event) { return; } UIManager uiManager = - UIManagerHelper.getUIManagerForReactTag(mReactApplicationContext, event.getViewTag()); + UIManagerHelper.getUIManager(mReactApplicationContext, event.getUIManagerType()); if (uiManager == null) { return; } @@ -720,11 +753,11 @@ private void updateNodes(List nodes) { if (mEventListenerInitializedForFabric && cyclesDetected == 0) { // TODO T71377544: investigate these SoftExceptions and see if we can remove entirely // or fix the root cause - ReactSoftException.logSoftException(TAG, new ReactNoCrashSoftException(ex)); + ReactSoftExceptionLogger.logSoftException(TAG, new ReactNoCrashSoftException(ex)); } else if (mEventListenerInitializedForFabric) { // TODO T71377544: investigate these SoftExceptions and see if we can remove entirely // or fix the root cause - ReactSoftException.logSoftException(TAG, new ReactNoCrashSoftException(ex)); + ReactSoftExceptionLogger.logSoftException(TAG, new ReactNoCrashSoftException(ex)); } else { throw ex; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java index 6302abe043e5f..c5e8a5ccbcfad 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java @@ -10,12 +10,14 @@ import android.os.Bundle; import android.os.Parcelable; import androidx.annotation.Nullable; +import com.facebook.proguard.annotations.DoNotStrip; import java.lang.reflect.Array; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; import java.util.Map; +@DoNotStrip public class Arguments { private static Object makeNativeObject(Object object) { if (object == null) { @@ -122,6 +124,7 @@ private static void addEntry(WritableNativeMap nativeMap, String key, Object val * The best way to think of this is a way to generate a Java representation of a json object, from * Java types which have a natural representation in json. */ + @DoNotStrip public static WritableNativeMap makeNativeMap(Map objects) { WritableNativeMap nativeMap = new WritableNativeMap(); if (objects == null) { @@ -134,6 +137,7 @@ public static WritableNativeMap makeNativeMap(Map objects) { } /** Like the above, but takes a Bundle instead of a Map. */ + @DoNotStrip public static WritableNativeMap makeNativeMap(Bundle bundle) { WritableNativeMap nativeMap = new WritableNativeMap(); if (bundle == null) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK index fcf60e3069658..0f54a3c0b0b57 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK @@ -20,6 +20,7 @@ rn_android_library( ["**/*.java"], exclude = INTERFACES, ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], proguard_config = "reactnative.pro", @@ -65,6 +66,7 @@ rn_android_library( rn_android_library( name = "interfaces", srcs = glob(INTERFACES), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], proguard_config = "reactnative.pro", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BackgroundExecutor.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BackgroundExecutor.java index 8b5d7c942542f..7ed0b3526f6e1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BackgroundExecutor.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BackgroundExecutor.java @@ -30,13 +30,14 @@ private void queueRunnable(Runnable runnable) { // but it's also cheap to leave it here. if (runnable == null) { - ReactSoftException.logSoftException(TAG, new ReactNoCrashSoftException("runnable is null")); + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException("runnable is null")); return; } final ExecutorService executorService = mExecutorService; if (executorService == null) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new ReactNoCrashSoftException("executorService is null")); return; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java index 283c365e910d6..a8bf93676f440 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java @@ -61,16 +61,18 @@ public boolean canOverrideExistingModule() { } @Override - public void onCatalystInstanceDestroy() { - // do nothing - } + public void onCatalystInstanceDestroy() {} public boolean hasConstants() { return false; } - // Cleanup Logic for TurboModules + /** + * The CatalystInstance is going away with Venice. Therefore, the TurboModule infra introduces the + * invalidate() method to allow NativeModules to clean up after themselves. + */ + @Override public void invalidate() { - // Do nothing + onCatalystInstanceDestroy(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java index 1a5067b7edb35..ddd1bee4f022b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -111,6 +111,8 @@ public interface CatalystInstance RuntimeExecutor getRuntimeExecutor(); + RuntimeScheduler getRuntimeScheduler(); + void addJSIModules(List jsiModules); /** diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java index 8717b59ce0652..060a20cfcd21c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java @@ -86,8 +86,6 @@ public String toString() { private final String mJsPendingCallsTitleForTrace = "pending_js_calls_instance" + sNextInstanceIdForTrace.getAndIncrement(); private volatile boolean mDestroyed = false; - private volatile boolean mNativeModulesThreadDestructionComplete = false; - private volatile boolean mJSThreadDestructionComplete = false; private final TraceListener mTraceListener; private final JavaScriptModuleRegistry mJSModuleRegistry; private final JSBundleLoader mJSBundleLoader; @@ -111,7 +109,8 @@ public String toString() { // C++ parts private final HybridData mHybridData; - private static native HybridData initHybrid(); + private static native HybridData initHybrid( + boolean enableRuntimeScheduler, boolean enableRuntimeSchedulerInTurboModule); public native CallInvokerHolderImpl getJSCallInvokerHolder(); @@ -126,7 +125,15 @@ private CatalystInstanceImpl( FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge."); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl"); - mHybridData = initHybrid(); + if (ReactFeatureFlags.enableRuntimeSchedulerInTurboModule + && !ReactFeatureFlags.enableRuntimeScheduler) { + Assertions.assertUnreachable(); + } + + mHybridData = + initHybrid( + ReactFeatureFlags.enableRuntimeScheduler, + ReactFeatureFlags.enableRuntimeSchedulerInTurboModule); mReactQueueConfiguration = ReactQueueConfigurationImpl.create( @@ -142,6 +149,11 @@ private CatalystInstanceImpl( FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge"); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge"); + + if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) { + warnOnLegacyNativeModuleSystemUse(); + } + initializeBridge( new BridgeCallback(this), jsExecutor, @@ -209,6 +221,8 @@ public void extendNativeModules(NativeModuleRegistry modules) { private native void jniExtendNativeModules( Collection javaModules, Collection cxxModules); + private native void warnOnLegacyNativeModuleSystemUse(); + private native void initializeBridge( ReactCallback callback, JavaScriptExecutor jsExecutor, @@ -570,9 +584,10 @@ public JavaScriptContextHolder getJavaScriptContextHolder() { return mJavaScriptContextHolder; } - @Override public native RuntimeExecutor getRuntimeExecutor(); + public native RuntimeScheduler getRuntimeScheduler(); + @Override public void addJSIModules(List jsiModules) { mJSIModuleRegistry.registerModules(jsiModules); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CxxModuleWrapperBase.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CxxModuleWrapperBase.java index 536434c410500..1d2814a1d545e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CxxModuleWrapperBase.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/CxxModuleWrapperBase.java @@ -38,7 +38,10 @@ public boolean canOverrideExistingModule() { } @Override - public void onCatalystInstanceDestroy() { + public void onCatalystInstanceDestroy() {} + + @Override + public void invalidate() { mHybridData.resetNative(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java index 09d20d6c9a3d8..52e9f43f64b23 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java @@ -8,6 +8,7 @@ package com.facebook.react.bridge; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.config.ReactFeatureFlags; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,5 +45,8 @@ public void notifyJSInstanceDestroy() { JSIModuleHolder moduleHolder = entry.getValue(); moduleHolder.notifyJSInstanceDestroy(); } + if (ReactFeatureFlags.enableReactContextCleanupFix) { + mModules.clear(); + } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java index 1720d0765da76..7528edce16d2e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java @@ -15,6 +15,7 @@ import androidx.annotation.Nullable; import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; import java.lang.reflect.Method; @@ -43,6 +44,7 @@ public class MethodDescriptor { private final ModuleHolder mModuleHolder; private final ArrayList mMethods; private final ArrayList mDescs; + private static final String TAG = JavaModuleWrapper.class.getSimpleName(); public JavaModuleWrapper(JSInstance jsInstance, ModuleHolder moduleHolder) { mJSInstance = jsInstance; @@ -122,6 +124,17 @@ public List getMethodDescriptors() { @DoNotStrip public @Nullable NativeMap getConstants() { + if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Calling getConstants() on Java NativeModule (name = \"" + + mModuleHolder.getName() + + "\", className = " + + mModuleHolder.getClassName() + + ").")); + } + if (!mModuleHolder.getHasConstants()) { return null; } @@ -153,10 +166,34 @@ public List getMethodDescriptors() { @DoNotStrip public void invoke(int methodId, ReadableNativeArray parameters) { + if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Calling method on Java NativeModule (name = \"" + + mModuleHolder.getName() + + "\", className = " + + mModuleHolder.getClassName() + + ").")); + } + if (mMethods == null || methodId >= mMethods.size()) { return; } + if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Calling " + + mDescs.get(methodId).name + + "() on Java NativeModule (name = \"" + + mModuleHolder.getName() + + "\", className = " + + mModuleHolder.getClassName() + + ").")); + } + mMethods.get(methodId).invoke(mJSInstance, parameters); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java index 0398c85a4731b..ad5709a07e033 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java @@ -107,7 +107,7 @@ public ModuleHolder(NativeModule nativeModule) { public synchronized void destroy() { if (mModule != null) { - mModule.onCatalystInstanceDestroy(); + mModule.invalidate(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java index 16939d0320962..11f7117f2e978 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModule.java @@ -47,6 +47,13 @@ interface NativeMethod { */ boolean canOverrideExistingModule(); - /** Called before {CatalystInstance#onHostDestroy} */ + /** + * Allow NativeModule to clean up. Called before {CatalystInstance#onHostDestroy} + * + * @deprecated use {@link #invalidate()} instead. + */ void onCatalystInstanceDestroy(); + + /** Allow NativeModule to clean up. Called before {CatalystInstance#onHostDestroy} */ + void invalidate(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModuleRegistry.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModuleRegistry.java index e45069d75adb5..809d915c01497 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModuleRegistry.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModuleRegistry.java @@ -8,6 +8,7 @@ package com.facebook.react.bridge; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.module.annotations.ReactModule; import com.facebook.systrace.Systrace; import java.util.ArrayList; @@ -20,6 +21,7 @@ public class NativeModuleRegistry { private final ReactApplicationContext mReactApplicationContext; private final Map mModules; + private final String TAG = NativeModuleRegistry.class.getSimpleName(); public NativeModuleRegistry( ReactApplicationContext reactApplicationContext, Map modules) { @@ -40,6 +42,17 @@ private ReactApplicationContext getReactApplicationContext() { ArrayList javaModules = new ArrayList<>(); for (Map.Entry entry : mModules.entrySet()) { if (!entry.getValue().isCxxModule()) { + if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Registering legacy NativeModule: Java NativeModule (name = \"" + + entry.getValue().getName() + + "\", className = " + + entry.getValue().getClassName() + + ").")); + } + javaModules.add(new JavaModuleWrapper(jsInstance, entry.getValue())); } } @@ -50,6 +63,16 @@ private ReactApplicationContext getReactApplicationContext() { ArrayList cxxModules = new ArrayList<>(); for (Map.Entry entry : mModules.entrySet()) { if (entry.getValue().isCxxModule()) { + if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Registering legacy NativeModule: Cxx NativeModule (name = \"" + + entry.getValue().getName() + + "\", className = " + + entry.getValue().getClassName() + + ").")); + } cxxModules.add(entry.getValue()); } } @@ -84,6 +107,9 @@ private ReactApplicationContext getReactApplicationContext() { for (ModuleHolder module : mModules.values()) { module.destroy(); } + if (ReactFeatureFlags.enableReactContextCleanupFix) { + mModules.clear(); + } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java index 859d227243f2e..dbc51207af220 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java @@ -23,6 +23,7 @@ import com.facebook.react.bridge.queue.ReactQueueConfiguration; import com.facebook.react.common.LifecycleState; import com.facebook.react.common.ReactConstants; +import com.facebook.react.config.ReactFeatureFlags; import java.lang.ref.WeakReference; import java.util.concurrent.CopyOnWriteArraySet; @@ -62,6 +63,7 @@ public class ReactContext extends ContextWrapper { private @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; private @Nullable NativeModuleCallExceptionHandler mExceptionHandlerWrapper; private @Nullable WeakReference mCurrentActivity; + private boolean mIsInitialized = false; public ReactContext(Context base) { super(base); @@ -76,7 +78,7 @@ public void initializeWithInstance(CatalystInstance catalystInstance) { throw new IllegalStateException("ReactContext has been already initialized"); } if (mDestroyed) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new IllegalStateException("Cannot initialize ReactContext after it has been destroyed.")); } @@ -87,11 +89,9 @@ public void initializeWithInstance(CatalystInstance catalystInstance) { initializeMessageQueueThreads(queueConfig); } - /** - * Initialize message queue threads using a ReactQueueConfiguration. TODO (janzer) T43898341 Make - * this package instead of public - */ - public void initializeMessageQueueThreads(ReactQueueConfiguration queueConfig) { + /** Initialize message queue threads using a ReactQueueConfiguration. */ + public synchronized void initializeMessageQueueThreads(ReactQueueConfiguration queueConfig) { + FLog.w(TAG, "initializeMessageQueueThreads() is called."); if (mUiMessageQueueThread != null || mNativeModulesMessageQueueThread != null || mJSMessageQueueThread != null) { @@ -100,6 +100,18 @@ public void initializeMessageQueueThreads(ReactQueueConfiguration queueConfig) { mUiMessageQueueThread = queueConfig.getUIQueueThread(); mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread(); mJSMessageQueueThread = queueConfig.getJSQueueThread(); + + /** TODO(T85807990): Fail fast if any of the threads is null. */ + if (mUiMessageQueueThread == null) { + throw new IllegalStateException("UI thread is null"); + } + if (mNativeModulesMessageQueueThread == null) { + throw new IllegalStateException("NativeModules thread is null"); + } + if (mJSMessageQueueThread == null) { + throw new IllegalStateException("JavaScript thread is null"); + } + mIsInitialized = true; } public void resetPerfStats() { @@ -169,7 +181,19 @@ public CatalystInstance getCatalystInstance() { return Assertions.assertNotNull(mCatalystInstance); } + /** + * This API has been deprecated due to naming consideration, please use hasActiveReactInstance() + * instead + * + * @return + */ + @Deprecated public boolean hasActiveCatalystInstance() { + return hasActiveReactInstance(); + } + + /** @return true if there is an non-null, alive react native instance */ + public boolean hasActiveReactInstance() { return mCatalystInstance != null && !mCatalystInstance.isDestroyed(); } @@ -183,7 +207,7 @@ public LifecycleState getLifecycleState() { public void addLifecycleEventListener(final LifecycleEventListener listener) { mLifecycleEventListeners.add(listener); - if (hasActiveCatalystInstance() || isBridgeless()) { + if (hasActiveReactInstance() || isBridgeless()) { switch (mLifecycleState) { case BEFORE_CREATE: case BEFORE_RESUME: @@ -296,6 +320,11 @@ public void destroy() { if (mCatalystInstance != null) { mCatalystInstance.destroy(); } + if (ReactFeatureFlags.enableReactContextCleanupFix) { + mLifecycleEventListeners.clear(); + mActivityEventListeners.clear(); + mWindowFocusEventListeners.clear(); + } } /** Should be called by the hosting Fragment in {@link Fragment#onActivityResult} */ @@ -334,10 +363,20 @@ public void runOnUiQueueThread(Runnable runnable) { } public void assertOnNativeModulesQueueThread() { + /** TODO(T85807990): Fail fast if the ReactContext isn't initialized */ + if (!mIsInitialized) { + throw new IllegalStateException( + "Tried to call assertOnNativeModulesQueueThread() on an uninitialized ReactContext"); + } Assertions.assertNotNull(mNativeModulesMessageQueueThread).assertIsOnThread(); } public void assertOnNativeModulesQueueThread(String message) { + /** TODO(T85807990): Fail fast if the ReactContext isn't initialized */ + if (!mIsInitialized) { + throw new IllegalStateException( + "Tried to call assertOnNativeModulesQueueThread(message) on an uninitialized ReactContext"); + } Assertions.assertNotNull(mNativeModulesMessageQueueThread).assertIsOnThread(message); } @@ -446,7 +485,7 @@ public JavaScriptContextHolder getJavaScriptContextHolder() { } public @Nullable JSIModule getJSIModule(JSIModuleType moduleType) { - if (!hasActiveCatalystInstance()) { + if (!hasActiveReactInstance()) { throw new IllegalStateException( "Unable to retrieve a JSIModule if CatalystInstance is not active."); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java index eb2111e4db3f1..132520c386b26 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java @@ -52,8 +52,7 @@ protected final ReactApplicationContext getReactApplicationContext() { */ @ThreadConfined(ANY) protected @Nullable final ReactApplicationContext getReactApplicationContextIfActiveOrWarn() { - if (mReactApplicationContext.hasActiveCatalystInstance() - || mReactApplicationContext.isBridgeless()) { + if (mReactApplicationContext.hasActiveReactInstance()) { return mReactApplicationContext; } @@ -64,7 +63,7 @@ protected final ReactApplicationContext getReactApplicationContext() { if (ReactBuildConfig.DEBUG) { FLog.w(tag, msg); } else { - ReactSoftException.logSoftException(tag, new RuntimeException(msg)); + ReactSoftExceptionLogger.logSoftException(tag, new RuntimeException(msg)); } return null; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactCxxErrorHandler.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactCxxErrorHandler.java new file mode 100644 index 0000000000000..f5efbf1fadf93 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactCxxErrorHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.bridge; + +import com.facebook.common.logging.FLog; +import com.facebook.proguard.annotations.DoNotStrip; +import java.lang.reflect.Method; + +@DoNotStrip +public class ReactCxxErrorHandler { + + private static Method mHandleErrorFunc; + private static Object mObject; + + @DoNotStrip + public static void setHandleErrorFunc(Object object, Method handleErrorFunc) { + mObject = object; + mHandleErrorFunc = handleErrorFunc; + } + + @DoNotStrip + // For use from within the C++ JReactCxxErrorHandler + private static void handleError(final String message) { + if (mHandleErrorFunc != null) { + try { + Object[] parameters = new Object[1]; + parameters[0] = new Exception(message); + mHandleErrorFunc.invoke(mObject, parameters); + } catch (Exception e) { + FLog.e("ReactCxxErrorHandler", "Failed to invole error hanlder function", e); + } + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java index 8c5f38d4d89c0..4c19185c72521 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java @@ -94,6 +94,12 @@ public enum ReactMarkerConstants { JAVASCRIPT_EXECUTOR_FACTORY_INJECT_END, LOAD_REACT_NATIVE_SO_FILE_START, LOAD_REACT_NATIVE_SO_FILE_END, + ROOT_VIEW_ON_MEASURE_START, + ROOT_VIEW_ON_MEASURE_END, + ROOT_VIEW_ATTACH_TO_REACT_INSTANCE_MANAGER_START, + ROOT_VIEW_ATTACH_TO_REACT_INSTANCE_MANAGER_END, + ROOT_VIEW_UPDATE_LAYOUT_SPECS_START, + ROOT_VIEW_UPDATE_LAYOUT_SPECS_END, // Fabric-specific constants below this line LOAD_REACT_NATIVE_FABRIC_SO_FILE_START, LOAD_REACT_NATIVE_FABRIC_SO_FILE_END, @@ -109,7 +115,11 @@ public enum ReactMarkerConstants { FABRIC_BATCH_EXECUTION_END, FABRIC_UPDATE_UI_MAIN_THREAD_START, FABRIC_UPDATE_UI_MAIN_THREAD_END, - // New markers used by bridgeless RN below this line - REACT_INSTANCE_INIT_START, - REACT_INSTANCE_INIT_END + // New markers used by bridge and bridgeless loading below this line + REACT_BRIDGE_LOADING_START, + REACT_BRIDGE_LOADING_END, + REACT_BRIDGELESS_LOADING_START, + REACT_BRIDGELESS_LOADING_END, + LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_START, + LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_END, } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactSoftException.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactSoftExceptionLogger.java similarity index 83% rename from android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactSoftException.java rename to android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactSoftExceptionLogger.java index fdb5205b1f608..a33590a83d090 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactSoftException.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactSoftExceptionLogger.java @@ -13,9 +13,7 @@ import java.util.concurrent.CopyOnWriteArrayList; @DoNotStrip -@Deprecated -// TODO T53446395: unify ReactSoftException with a more generic exception-handling utility -public class ReactSoftException { +public class ReactSoftExceptionLogger { public interface ReactSoftExceptionListener { void logSoftException(final String category, final Throwable cause); } @@ -52,4 +50,11 @@ public static void logSoftException(final String category, final Throwable cause FLog.e(category, "Unhandled SoftException", cause); } } + + @DoNotStrip + // For use from within the C++ JReactSoftExceptionLogger + private static void logNoThrowSoftExceptionWithMessage( + final String category, final String message) { + logSoftException(category, new ReactNoCrashSoftException(message)); + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java index ae471c2a8c438..a2e4319224abc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java @@ -187,12 +187,62 @@ public int getInt(@NonNull String name) { @Override public @NonNull Iterator> getEntryIterator() { - return getLocalMap().entrySet().iterator(); + if (mKeys == null) { + mKeys = Assertions.assertNotNull(importKeys()); + } + final String[] iteratorKeys = mKeys; + final Object[] iteratorValues = Assertions.assertNotNull(importValues()); + return new Iterator>() { + int currentIndex = 0; + + @Override + public boolean hasNext() { + return currentIndex < iteratorKeys.length; + } + + @Override + public Map.Entry next() { + final int index = currentIndex++; + return new Map.Entry() { + @Override + public String getKey() { + return iteratorKeys[index]; + } + + @Override + public Object getValue() { + return iteratorValues[index]; + } + + @Override + public Object setValue(Object value) { + throw new UnsupportedOperationException( + "Can't set a value while iterating over a ReadableNativeMap"); + } + }; + } + }; } @Override public @NonNull ReadableMapKeySetIterator keySetIterator() { - return new ReadableNativeMapKeySetIterator(this); + if (mKeys == null) { + mKeys = Assertions.assertNotNull(importKeys()); + } + final String[] iteratorKeys = mKeys; + return new ReadableMapKeySetIterator() { + int currentIndex = 0; + + @Override + public boolean hasNextKey() { + return currentIndex < iteratorKeys.length; + } + + @Override + public String nextKey() { + return iteratorKeys[currentIndex++]; + } + }; } @Override diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/RuntimeScheduler.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/RuntimeScheduler.java new file mode 100644 index 0000000000000..7270c65c6bfa1 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/RuntimeScheduler.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.bridge; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; + +/** A Java holder for a C++ RuntimeScheduler. */ +public class RuntimeScheduler { + + @DoNotStrip private HybridData mHybridData; + + public RuntimeScheduler(HybridData hybridData) { + mHybridData = hybridData; + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/SoftAssertions.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/SoftAssertions.java index 434ad411ad156..4bccdfd1dd070 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/SoftAssertions.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/SoftAssertions.java @@ -20,31 +20,32 @@ public class SoftAssertions { /** * Throw {@link AssertionException} with a given message. Use this method surrounded with {@code * if} block with assert condition in case you plan to do string concatenation to produce the - * message. This logs an assertion with ReactSoftException, which decides whether or not to + * message. This logs an assertion with ReactSoftExceptionLogger, which decides whether or not to * actually throw. */ public static void assertUnreachable(String message) { - ReactSoftException.logSoftException("SoftAssertions", new AssertionException(message)); + ReactSoftExceptionLogger.logSoftException("SoftAssertions", new AssertionException(message)); } /** * Asserts the given condition, throwing an {@link AssertionException} if the condition doesn't - * hold. This logs an assertion with ReactSoftException, which decides whether or not to actually - * throw. + * hold. This logs an assertion with ReactSoftExceptionLogger, which decides whether or not to + * actually throw. */ public static void assertCondition(boolean condition, String message) { if (!condition) { - ReactSoftException.logSoftException("SoftAssertions", new AssertionException(message)); + ReactSoftExceptionLogger.logSoftException("SoftAssertions", new AssertionException(message)); } } /** * Asserts that the given Object isn't null, throwing an {@link AssertionException} if it was. - * This logs an assertion with ReactSoftException, which decides whether or not to actually throw. + * This logs an assertion with ReactSoftExceptionLogger, which decides whether or not to actually + * throw. */ public static T assertNotNull(@Nullable T instance) { if (instance == null) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( "SoftAssertions", new AssertionException("Expected object to not be null!")); } return instance; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java index 74af261f5b47c..50ab07a1b3b6f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java @@ -14,12 +14,14 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.facebook.infer.annotation.ThreadConfined; +import java.util.List; public interface UIManager extends JSIModule, PerformanceCounter { - /** Registers a new root view. */ + /** Registers a new root view. @Deprecated call startSurface instead */ @UiThread @ThreadConfined(UI) + @Deprecated int addRootView( final T rootView, WritableMap initialProps, @Nullable String initialUITemplate); @@ -83,7 +85,7 @@ void updateRootLayoutSpecs( * layout-related propertied won't be handled properly. Make sure you know what you're doing * before calling this method :) * - * @param tag {@link int} that identifies the view that will be updated + * @param reactTag {@link int} that identifies the view that will be updated * @param props {@link ReadableMap} props that should be immediately updated in view */ @UiThread @@ -115,6 +117,16 @@ void updateRootLayoutSpecs( */ void removeUIManagerEventListener(UIManagerListener listener); + /** + * Resolves a view based on its reactTag. Do not mutate properties on this view that are already + * managed by React, as there are no guarantees this changes will be preserved. + * + * @throws IllegalViewOperationException if tag could not be resolved. + * @param reactTag tag + * @return view if found + */ + View resolveView(int reactTag); + /** * This method dispatches events from RN Android code to JS. The delivery of this event will not * be queued in EventDispatcher class. @@ -123,10 +135,33 @@ void updateRootLayoutSpecs( * @param eventName name of the event * @param event parameters */ + @Deprecated void receiveEvent(int reactTag, String eventName, @Nullable WritableMap event); + /** + * This method dispatches events from RN Android code to JS. The delivery of this event will not + * be queued in EventDispatcher class. + * + * @param surfaceId + * @param reactTag tag + * @param eventName name of the event + * @param event parameters + */ + void receiveEvent(int surfaceId, int reactTag, String eventName, @Nullable WritableMap event); + /** Resolves Direct Event name exposed to JS from the one known to the Native side. */ @Deprecated @Nullable String resolveCustomDirectEventName(@Nullable String eventName); + + /** + * Helper method to pre-initialize view managers. When using Native ViewConfigs this method will + * also pre-compute the constants for a view manager. The purpose is to ensure that we don't block + * for getting the constants for view managers during initial rendering of a surface. + * + * @deprecated this method will be removed in the future + * @param viewManagerNames {@link List } names of ViewManagers + */ + @Deprecated + void preInitializeViewManagers(List viewManagerNames); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/common/BUCK index ef52fb6fb78ab..710a6095cb24e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/common/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/BUCK @@ -1,7 +1,8 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_build_config", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "HERMES_BYTECODE_VERSION", "react_native_dep", "rn_android_build_config", "rn_android_library") SUB_PROJECTS = [ "network/**/*", + "mapbuffer/**/*", ] rn_android_library( @@ -10,6 +11,7 @@ rn_android_library( ["**/*.java"], exclude = SUB_PROJECTS, ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -39,6 +41,7 @@ rn_android_build_config( package = "com.facebook.react", values = [ "boolean IS_INTERNAL_BUILD = true", + "int HERMES_BYTECODE_VERSION = {}".format(HERMES_BYTECODE_VERSION), ], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java new file mode 100644 index 0000000000000..91e24105e1f37 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.common; + +/** + * Interface for handling a surface in React Native. In mobile platform a surface can be any + * container that holds some {@link View}. For example, a Dialog can be a surface to wrap content + * view object as needed. In VR platform, a surface is provided by Shell panel app sdk, which + * requires custom logic to show/hide. NativeModules requires a surface will delegate interactions + * with the surface to a SurfaceDelegate. + */ +public interface SurfaceDelegate { + /** + * Create the React content view that uses the appKey as the React application name + * + * @param appKey + */ + void createContentView(String appKey); + + /** + * Check if the content view is created and ready to be shown + * + * @return true if the content view is ready to be shown + */ + boolean isContentViewReady(); + + /** Destroy the React content view to avoid memory leak */ + void destroyContentView(); + + /** Show the surface containing the React content view */ + void show(); + + /** Hide the surface containing the React content view */ + void hide(); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.java b/android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.java new file mode 100644 index 0000000000000..938f4b1e3e30d --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.common; + +/** + * Factory to create a {@link SurfaceDelegate}. The moduleName is needed to help the factory decide + * which surface to return {@link SurfaceDelegate} that the given module should use to interact + * with. + */ +public interface SurfaceDelegateFactory { + /** + * Create a {@link SurfaceDelegate} instance which is used to interact with a surface of platform + * the app is running in. + * + * @param moduleName the module name that will be using the surface + * @return {@link SurfaceDelegate} instance + */ + SurfaceDelegate createSurfaceDelegate(String moduleName); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/build/ReactBuildConfig.java b/android/ReactAndroid/src/main/java/com/facebook/react/common/build/ReactBuildConfig.java index a90ec674e12ed..093bd88c580a4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/common/build/ReactBuildConfig.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/build/ReactBuildConfig.java @@ -20,4 +20,5 @@ public class ReactBuildConfig { public static final boolean DEBUG = BuildConfig.DEBUG; public static final boolean IS_INTERNAL_BUILD = BuildConfig.IS_INTERNAL_BUILD; public static final int EXOPACKAGE_FLAGS = BuildConfig.EXOPACKAGE_FLAGS; + public static final int HERMES_BYTECODE_VERSION = BuildConfig.HERMES_BYTECODE_VERSION; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/BUCK new file mode 100644 index 0000000000000..a552d6027fe00 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/BUCK @@ -0,0 +1,29 @@ +load("//tools/build_defs/oss:rn_defs.bzl", "FBJNI_TARGET", "react_native_dep", "react_native_target", "rn_android_library") + +rn_android_library( + name = "mapbuffer", + srcs = glob([ + "*.java", + ]), + autoglob = False, + is_androidx = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + provided_deps = [], + required_for_source_only_abi = True, + visibility = [ + "PUBLIC", + ], + deps = [ + FBJNI_TARGET, + react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"), + react_native_target("java/com/facebook/react/common/mapbuffer/jni:jni"), + react_native_dep("libraries/fbjni:java"), + react_native_dep("third-party/android/androidx:annotation"), + react_native_dep("third-party/java/infer-annotations:infer-annotations"), + react_native_target("java/com/facebook/react/common:common"), + # dependencies used for systraces + react_native_dep("java/com/facebook/systrace:systrace"), + react_native_target("java/com/facebook/react/bridge:bridge"), + ], + exported_deps = [], +) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java new file mode 100644 index 0000000000000..f38ae42b07ad7 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java @@ -0,0 +1,347 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.common.mapbuffer; + +import androidx.annotation.Nullable; +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Iterator; + +/** + * TODO T83483191: add documentation. + * + *

NOTE: {@link ReadableMapBuffer} is NOT thread safe. + */ +public class ReadableMapBuffer implements Iterable { + + static { + ReadableMapBufferSoLoader.staticInit(); + } + + // Value used to verify if the data is serialized with LittleEndian order. + private static final int ALIGNMENT = 0xFE; + + // 8 bytes = 2 (alignment) + 2 (count) + 4 (size) + private static final int HEADER_SIZE = 8; + + // key size = 2 bytes + private static final int KEY_SIZE = 2; + + // 10 bytes = 2 bytes key + 8 bytes value + private static final int BUCKET_SIZE = 10; + + private static final int INT_SIZE = 4; + + // TODO T83483191: consider moving short to INTs, we are doing extra cast operations just because + // of short java operates with int + private static final int SHORT_SIZE = 2; + + private static final short SHORT_ONE = (short) 1; + + @Nullable ByteBuffer mBuffer = null; + + // Size of the Serialized Data + @SuppressWarnings("unused") + private int mSizeOfData = 0; + + // Amount of items serialized on the ByteBuffer + @SuppressWarnings("unused") + private short mCount = 0; + + private ReadableMapBuffer(HybridData hybridData) { + mHybridData = hybridData; + } + + private ReadableMapBuffer(ByteBuffer buffer) { + mBuffer = buffer; + readHeader(); + } + + private native ByteBuffer importByteBufferAllocateDirect(); + + private native ByteBuffer importByteBuffer(); + + @SuppressWarnings("unused") + @DoNotStrip + @Nullable + private HybridData mHybridData; + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (mHybridData != null) { + mHybridData.resetNative(); + } + } + + private int getKeyOffsetForBucketIndex(int bucketIndex) { + return HEADER_SIZE + BUCKET_SIZE * bucketIndex; + } + + private int getValueOffsetForKey(short key) { + importByteBufferAndReadHeader(); + int bucketIndex = getBucketIndexForKey(key); + if (bucketIndex == -1) { + // TODO T83483191: Add tests + throw new IllegalArgumentException("Unable to find key: " + key); + } + assertKeyExists(key, bucketIndex); + return getKeyOffsetForBucketIndex(bucketIndex) + KEY_SIZE; + } + + // returns the relative offset of the first byte of dynamic data + private int getOffsetForDynamicData() { + // TODO T83483191: check if there's dynamic data? + return getKeyOffsetForBucketIndex(mCount); + } + + /** + * @param key Key to search for + * @return the "bucket index" for a key or -1 if not found. It uses a binary search algorithm + * (log(n)) + */ + private int getBucketIndexForKey(short key) { + short lo = 0; + short hi = (short) (getCount() - SHORT_ONE); + while (lo <= hi) { + final short mid = (short) ((lo + hi) >>> SHORT_ONE); + final short midVal = readKey(getKeyOffsetForBucketIndex(mid)); + if (midVal < key) { + lo = (short) (mid + SHORT_ONE); + } else if (midVal > key) { + hi = (short) (mid - SHORT_ONE); + } else { + return mid; + } + } + return -1; + } + + private short readKey(int position) { + return mBuffer.getShort(position); + } + + private double readDoubleValue(int bufferPosition) { + return mBuffer.getDouble(bufferPosition); + } + + private int readIntValue(int bufferPosition) { + return mBuffer.getInt(bufferPosition); + } + + private boolean readBooleanValue(int bufferPosition) { + return readIntValue(bufferPosition) == 1; + } + + private String readStringValue(int bufferPosition) { + int offset = getOffsetForDynamicData() + mBuffer.getInt(bufferPosition); + + int sizeOfString = mBuffer.getInt(offset); + byte[] result = new byte[sizeOfString]; + + int stringOffset = offset + INT_SIZE; + + mBuffer.position(stringOffset); + mBuffer.get(result, 0, sizeOfString); + + return new String(result); + } + + private ReadableMapBuffer readMapBufferValue(int position) { + int offset = getOffsetForDynamicData() + mBuffer.getInt(position); + + int sizeMapBuffer = mBuffer.getInt(offset); + byte[] buffer = new byte[sizeMapBuffer]; + + int bufferOffset = offset + INT_SIZE; + + mBuffer.position(bufferOffset); + mBuffer.get(buffer, 0, sizeMapBuffer); + + return new ReadableMapBuffer(ByteBuffer.wrap(buffer)); + } + + private void readHeader() { + // byte order + short storedAlignment = mBuffer.getShort(); + if (storedAlignment != ALIGNMENT) { + mBuffer.order(ByteOrder.LITTLE_ENDIAN); + } + // count + mCount = mBuffer.getShort(); + // size + mSizeOfData = mBuffer.getInt(); + } + + /** + * Binary search of the key inside the mapBuffer (log(n)). + * + * @param key Key to search for + * @return true if and only if the Key received as a parameter is stored in the MapBuffer. + */ + public boolean hasKey(short key) { + // TODO T83483191: Add tests + return getBucketIndexForKey(key) != -1; + } + + /** @return amount of elements stored into the MapBuffer */ + public short getCount() { + importByteBufferAndReadHeader(); + return mCount; + } + + /** + * @param key {@link int} representing the key + * @return return the int associated to the Key received as a parameter. + */ + public int getInt(short key) { + // TODO T83483191: extract common code of "get methods" + return readIntValue(getValueOffsetForKey(key)); + } + + /** + * @param key {@link int} representing the key + * @return return the double associated to the Key received as a parameter. + */ + public double getDouble(short key) { + return readDoubleValue(getValueOffsetForKey(key)); + } + + /** + * @param key {@link int} representing the key + * @return return the int associated to the Key received as a parameter. + */ + public String getString(short key) { + return readStringValue(getValueOffsetForKey(key)); + } + + public boolean getBoolean(short key) { + return readBooleanValue(getValueOffsetForKey(key)); + } + + /** + * @param key {@link int} representing the key + * @return return the int associated to the Key received as a parameter. + */ + public ReadableMapBuffer getMapBuffer(short key) { + return readMapBufferValue(getValueOffsetForKey(key)); + } + + /** + * Import ByteBuffer from C++, read the header and move the current cursor at the start of the + * payload. + */ + private ByteBuffer importByteBufferAndReadHeader() { + if (mBuffer != null) { + return mBuffer; + } + + // mBuffer = importByteBufferAllocateDirect(); + mBuffer = importByteBuffer(); + + readHeader(); + return mBuffer; + } + + private void assertKeyExists(short key, int bucketIndex) { + short storedKey = mBuffer.getShort(getKeyOffsetForBucketIndex(bucketIndex)); + if (storedKey != key) { + throw new IllegalStateException( + "Stored key doesn't match parameter - expected: " + key + " - found: " + storedKey); + } + } + + @Override + public int hashCode() { + ByteBuffer byteBuffer = importByteBufferAndReadHeader(); + byteBuffer.rewind(); + return byteBuffer.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof ReadableMapBuffer)) { + return false; + } + + ReadableMapBuffer other = (ReadableMapBuffer) obj; + ByteBuffer thisByteBuffer = importByteBufferAndReadHeader(); + ByteBuffer otherByteBuffer = other.importByteBufferAndReadHeader(); + if (thisByteBuffer == otherByteBuffer) { + return true; + } + thisByteBuffer.rewind(); + otherByteBuffer.rewind(); + return thisByteBuffer.equals(otherByteBuffer); + } + + /** @return an {@link Iterator} for the entries of this MapBuffer. */ + @Override + public Iterator iterator() { + return new Iterator() { + short current = 0; + short last = (short) (getCount() - SHORT_ONE); + + @Override + public boolean hasNext() { + return current <= last; + } + + @Override + public MapBufferEntry next() { + return new MapBufferEntry(getKeyOffsetForBucketIndex(current++)); + } + }; + } + + /** This class represents an Entry of the {@link ReadableMapBuffer} class. */ + public class MapBufferEntry { + private final int mBucketOffset; + + private MapBufferEntry(int position) { + mBucketOffset = position; + } + + /** @return a {@link short} that represents the key of this {@link MapBufferEntry}. */ + public short getKey() { + return readKey(mBucketOffset); + } + + /** @return the double value that is stored in this {@link MapBufferEntry}. */ + public double getDouble(double defaultValue) { + // TODO T83483191 Extend serialization of MapBuffer to add type checking + // TODO T83483191 Extend serialization of MapBuffer to return null if there's no value + // stored in this MapBufferEntry. + return readDoubleValue(mBucketOffset + KEY_SIZE); + } + + /** @return the int value that is stored in this {@link MapBufferEntry}. */ + public int getInt(int defaultValue) { + return readIntValue(mBucketOffset + KEY_SIZE); + } + + /** @return the boolean value that is stored in this {@link MapBufferEntry}. */ + public boolean getBoolean(boolean defaultValue) { + return readBooleanValue(mBucketOffset + KEY_SIZE); + } + + /** @return the String value that is stored in this {@link MapBufferEntry}. */ + public @Nullable String getString() { + return readStringValue(mBucketOffset + KEY_SIZE); + } + + /** + * @return the {@link ReadableMapBuffer} value that is stored in this {@link MapBufferEntry}. + */ + public @Nullable ReadableMapBuffer getReadableMapBuffer() { + return readMapBufferValue(mBucketOffset + KEY_SIZE); + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java new file mode 100644 index 0000000000000..d38c3a11ea3a5 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.common.mapbuffer; + +import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; + +import com.facebook.react.bridge.ReactMarker; +import com.facebook.react.bridge.ReactMarkerConstants; +import com.facebook.soloader.SoLoader; +import com.facebook.systrace.Systrace; + +public class ReadableMapBufferSoLoader { + private static volatile boolean sDidInit = false; + + public static void staticInit() { + if (sDidInit) { + return; + } + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "ReadableMapBufferSoLoader.staticInit::load:mapbufferjni"); + ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_START); + SoLoader.loadLibrary("mapbufferjni"); + ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_END); + Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); + sDidInit = true; + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/Android.mk new file mode 100644 index 0000000000000..f29eb0137c001 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/Android.mk @@ -0,0 +1,39 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := mapbufferjni + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/react/common/mapbuffer/*.cpp) + +LOCAL_SHARED_LIBRARIES := libreactconfig libyoga libglog libfb libfbjni libglog_init libfolly_json libfolly_futures libreact_utils libreact_render_mapbuffer libreact_debug + +LOCAL_STATIC_LIBRARIES := + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"Fabric\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,fbgloginit) +$(call import-module,folly) +$(call import-module,fb) +$(call import-module,fbjni) +$(call import-module,yogajni) +$(call import-module,glog) + +$(call import-module,react/utils) +$(call import-module,react/debug) +$(call import-module,react/config) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/BUCK new file mode 100644 index 0000000000000..c2ac7a7ffbc68 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/BUCK @@ -0,0 +1,28 @@ +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob") + +rn_xplat_cxx_library( + name = "jni", + srcs = glob(["**/*.cpp"]), + headers = glob(["**/*.h"]), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("react/common/mapbuffer", "*.h"), + ], + prefix = "react/common/mapbuffer", + ), + fbandroid_allow_jni_merging = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + platforms = ANDROID, + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + soname = "libmapbufferjni.$(ext)", + visibility = ["PUBLIC"], + deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), + react_native_xplat_target("react/debug:debug"), + FBJNI_TARGET, + ], +) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/OnLoad.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/OnLoad.cpp new file mode 100644 index 0000000000000..76e0aa1a2c877 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/OnLoad.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include "ReadableMapBuffer.h" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize( + vm, [] { facebook::react::ReadableMapBuffer::registerNatives(); }); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.cpp new file mode 100644 index 0000000000000..83d856abd0a9f --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ReadableMapBuffer.h" + +namespace facebook { +namespace react { + +void ReadableMapBuffer::registerNatives() { + registerHybrid({ + makeNativeMethod("importByteBuffer", ReadableMapBuffer::importByteBuffer), + makeNativeMethod( + "importByteBufferAllocateDirect", + ReadableMapBuffer::importByteBufferAllocateDirect), + }); +} + +jni::local_ref +ReadableMapBuffer::importByteBufferAllocateDirect() { + // TODO T83483191: Using this method is safer than "importByteBuffer" because + // ByteBuffer memory will be deallocated once the "Java ByteBuffer" is + // deallocated. Next steps: + // - Validate perf of this method vs importByteBuffer + // - Validate that there's no leaking of memory + react_native_assert( + (serializedData_ != nullptr && serializedDataSize_ != 0) && + "Error serializedData_ is not initialized"); + auto ret = jni::JByteBuffer::allocateDirect(serializedDataSize_); + // TODO T83483191: avoid allocating serializedData_ when using + // JByteBuffer::allocateDirect + std::memcpy( + ret->getDirectBytes(), (void *)serializedData_, serializedDataSize_); + + // Deallocate serializedData_ since it's not necessary anymore + delete[] serializedData_; + serializedData_ = nullptr; + serializedDataSize_ = 0; + return ret; +} + +jni::JByteBuffer::javaobject ReadableMapBuffer::importByteBuffer() { + // TODO T83483191: Reevaluate what's the best approach here (allocateDirect vs + // DirectByteBuffer). + // + // On this method we should: + // - Review deallocation of serializedData (we are probably leaking + // _serializedData now). + // - Consider using allocate() or allocateDirect() methods from java instead + // of newDirectByteBuffer (to simplify de/allocation) : + // https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/fbandroid/libraries/fbjni/cxx/fbjni/ByteBuffer.cpp + // - Add flags to describe if the data was already 'imported' + // - Long-term: Consider creating a big ByteBuffer that can be re-used to + // transfer data of multitple Maps + return static_cast( + jni::Environment::current()->NewDirectByteBuffer( + (void *)serializedData_, serializedDataSize_)); +} + +jni::local_ref +ReadableMapBuffer::createWithContents(MapBuffer &&map) { + return newObjectCxxArgs(std::move(map)); +} + +ReadableMapBuffer::~ReadableMapBuffer() { + if (serializedData_ != nullptr) { + delete[] serializedData_; + serializedData_ = nullptr; + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.h b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.h new file mode 100644 index 0000000000000..b3f753a67b171 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/jni/react/common/mapbuffer/ReadableMapBuffer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace facebook { +namespace react { + +class ReadableMapBuffer : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = + "Lcom/facebook/react/common/mapbuffer/ReadableMapBuffer;"; + + static void registerNatives(); + + static jni::local_ref createWithContents(MapBuffer &&map); + + jni::local_ref importByteBufferAllocateDirect(); + + jni::JByteBuffer::javaobject importByteBuffer(); + + ~ReadableMapBuffer(); + + private: + uint8_t *serializedData_ = nullptr; + + int32_t serializedDataSize_ = 0; + + friend HybridBase; + + explicit ReadableMapBuffer(MapBuffer &&map) { + serializedDataSize_ = map.getBufferSize(); + react_native_assert( + (serializedDataSize_ != 0) && "Error no content in map"); + serializedData_ = new Byte[serializedDataSize_]; + map.copy(serializedData_); + } +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK index 25f39b59688c2..2eca2adf3f72c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "network", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/config/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/config/BUCK index 6120fdec936c2..f5ea5a7dd8065 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/config/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/config/BUCK @@ -1,12 +1,14 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_library") rn_android_library( name = "config", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], deps = [ + react_native_dep("java/com/facebook/proguard/annotations:annotations"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/android/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index 04a8a88eb5ca6..446ea326ac96c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -7,6 +7,8 @@ package com.facebook.react.config; +import com.facebook.proguard.annotations.DoNotStripAny; + /** * Hi there, traveller! This configuration class is not meant to be used by end-users of RN. It * contains mainly flags for features that are either under active development and not ready for @@ -14,8 +16,8 @@ * *

These values are safe defaults and should not require manual changes. */ +@DoNotStripAny public class ReactFeatureFlags { - /** * Should this application use TurboModules? If yes, then any module that inherits {@link * com.facebook.react.turbomodule.core.interfaces.TurboModule} will NOT be passed in to C++ @@ -23,58 +25,84 @@ public class ReactFeatureFlags { */ public static volatile boolean useTurboModules = false; - /* - * This feature flag enables logs for Fabric - */ - public static boolean enableFabricLogs = false; - /** - * Should this application use a {@link com.facebook.react.uimanager.ViewManagerDelegate} (if - * provided) to update the view properties. If {@code false}, then the generated {@code - * ...$$PropsSetter} class will be used instead. + * Should this application use the new (Fabric) Renderer? If yes, all rendering in this app will + * use Fabric instead of the legacy renderer. */ - public static boolean useViewManagerDelegates = false; + public static volatile boolean enableFabricRenderer = false; /** - * Should this application use a {@link com.facebook.react.uimanager.ViewManagerDelegate} (if - * provided) to execute the view commands. If {@code false}, then {@code receiveCommand} method - * inside view manager will be called instead. + * After TurboModules and Fabric are enabled, we need to ensure that the legacy NativeModule isn't + * isn't used. So, turn this flag on to trigger warnings whenever the legacy NativeModule system + * is used. */ - public static boolean useViewManagerDelegatesForCommands = false; + public static volatile boolean warnOnLegacyNativeModuleSystemUse = false; + + /** Should we dispatch TurboModule methods with promise returns to the NativeModules thread? */ + public static volatile boolean enableTurboModulePromiseAsyncDispatch = false; /** - * This react flag enables a custom algorithm for the getChildVisibleRect() method in the classes - * ReactViewGroup, ReactHorizontalScrollView and ReactScrollView. + * Experiment: * - *

This new algorithm clip child rects if overflow is set to ViewProps.HIDDEN. More details in - * https://github.com/facebook/react-native/issues/23870 and - * https://github.com/facebook/react-native/pull/26334 + *

Bridge and Bridgeless mode can run concurrently. This means that there can be two + * TurboModule systems alive at the same time. * - *

The react flag is disabled by default because this is increasing ANRs (T57363204) + *

The TurboModule system stores all JS callbacks in a global LongLivedObjectCollection. This + * collection is cleared when the JS VM is torn down. Implication: Tearing down the bridge JSVM + * invalidates the bridgeless JSVM's callbacks, and vice versa. + * + *

useGlobalCallbackCleanupScopeUsingRetainJSCallback => Use a retainJSCallbacks lambda to + * store jsi::Functions into the global LongLivedObjectCollection + * + *

useTurboModuleManagerCallbackCleanupScope => Use a retainJSCallbacks labmda to store + * jsi::Functions into a LongLivedObjectCollection owned by the TurboModuleManager */ - public static boolean clipChildRectsIfOverflowIsHidden = false; + public static boolean useGlobalCallbackCleanupScopeUsingRetainJSCallback = false; - /** - * Temporary feature flat to control a fix in the transition to layoutOnlyViews TODO T61185028: - * remove this when bug is fixed - */ - public static boolean enableTransitionLayoutOnlyViewCleanup = false; + public static boolean useTurboModuleManagerCallbackCleanupScope = false; + + /** This feature flag enables logs for Fabric */ + public static boolean enableFabricLogs = false; /** Feature flag to configure eager initialization of Fabric */ public static boolean eagerInitializeFabric = false; - /** Feature flag to use stopSurface when ReactRootView is unmounted. */ - public static boolean enableStopSurfaceOnRootViewUnmount = false; + /** Enables Static ViewConfig in RN Android native code. */ + public static boolean enableExperimentalStaticViewConfigs = false; + + public static boolean enableRuntimeScheduler = false; + + public static boolean enableRuntimeSchedulerInTurboModule = false; + + /** Enables a more aggressive cleanup during destruction of ReactContext */ + public static boolean enableReactContextCleanupFix = false; + + /** Feature flag to configure eager initialization of MapBuffer So file */ + public static boolean enableEagerInitializeMapBufferSoFile = false; + + private static boolean mapBufferSerializationEnabled = false; + + /** Enables or disables MapBuffer Serialization */ + public static void setMapBufferSerializationEnabled(boolean enabled) { + mapBufferSerializationEnabled = enabled; + } + + public static boolean isMapBufferSerializationEnabled() { + return mapBufferSerializationEnabled; + } + + /** Enables Fabric for LogBox */ + public static boolean enableFabricInLogBox = false; + + public static boolean enableLockFreeEventDispatcher = false; + + public static boolean enableAggressiveEventEmitterCleanup = false; - /** Use experimental SetState retry mechanism in view? */ - public static boolean enableExperimentalStateUpdateRetry = false; + public static boolean insertZReorderBarriersOnViewGroupChildren = true; - /** Enable caching of Spannable objects using equality of ReadableNativeMaps */ - public static boolean enableSpannableCacheByReadableNativeMapEquality = true; + public static boolean enableScrollViewSnapToAlignmentProp = true; - /** Disable customDrawOrder in ReactViewGroup under Fabric only. */ - public static boolean disableCustomDrawOrderFabric = false; + public static boolean useDispatchUniqueForCoalescableEvents = false; - /** Potential bugfix for crashes caused by mutating the view hierarchy during onDraw. */ - public static boolean enableDrawMutationFix = true; + public static boolean useUpdatedTouchPreprocessing = false; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK index a2113e6fc542a..f47c5129460f1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "devsupport", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], manifest = "AndroidManifest.xml", @@ -39,13 +40,14 @@ rn_android_library( react_native_target("res:devsupport"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) rn_android_library( name = "interfaces", srcs = glob(["interfaces/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -55,6 +57,7 @@ rn_android_library( react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/modules/debug:interfaces"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java new file mode 100644 index 0000000000000..62ae6f8dd28ce --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.devsupport; + +import android.content.Context; +import android.widget.Toast; +import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; +import com.facebook.debug.holder.PrinterHolder; +import com.facebook.debug.tags.ReactDebugOverlayTags; +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.CatalystInstance; +import com.facebook.react.bridge.JSBundleLoader; +import com.facebook.react.bridge.JavaJSExecutor; +import com.facebook.react.bridge.JavaScriptExecutorFactory; +import com.facebook.react.bridge.ReactMarker; +import com.facebook.react.bridge.ReactMarkerConstants; +import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.SurfaceDelegateFactory; +import com.facebook.react.common.futures.SimpleSettableFuture; +import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; +import com.facebook.react.devsupport.interfaces.DevOptionHandler; +import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback; +import com.facebook.react.packagerconnection.RequestHandler; +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Interface for accessing and interacting with development features. Following features + * are supported through this manager class: + * 1) Displaying JS errors (aka RedBox) + * 2) Displaying developers menu (Reload JS, Debug JS) + * 3) Communication with developer server in order to download updated JS bundle + * 4) Starting/stopping broadcast receiver for js reload signals + * 5) Starting/stopping motion sensor listener that recognize shake gestures which in turn may + * trigger developers menu. + * 6) Launching developers settings view + * + * This class automatically monitors the state of registered views and activities to which they are + * bound to make sure that we don't display overlay or that we we don't listen for sensor events + * when app is backgrounded. + * + * {@link com.facebook.react.ReactInstanceManager} implementation is responsible for instantiating + * this class as well as for populating with a reference to {@link CatalystInstance} whenever + * instance manager recreates it (through {@link #onNewReactContextCreated). Also, instance manager + * is responsible for enabling/disabling dev support in case when app is backgrounded or when all + * the views has been detached from the instance (through {@link #setDevSupportEnabled} method). + * + * IMPORTANT: In order for developer support to work correctly it is required that the + * manifest of your application contain the following entries: + * {@code } + * {@code } + */ +public class BridgeDevSupportManager extends DevSupportManagerBase { + + public boolean mIsSamplingProfilerEnabled = false; + + public BridgeDevSupportManager(Context applicationContext, ReactInstanceDevHelper reactInstanceManagerHelper, @Nullable String packagerPathForJSBundleName, boolean enableOnCreate, @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, @Nullable Map customPackagerCommandHandlers, @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { + super(applicationContext, reactInstanceManagerHelper, packagerPathForJSBundleName, enableOnCreate, redBoxHandler, devBundleDownloadListener, minNumShakes, customPackagerCommandHandlers, surfaceDelegateFactory); + if (getDevSettings().isStartSamplingProfilerOnInit()) { + // Only start the profiler. If its already running, there is an error + if (!mIsSamplingProfilerEnabled) { + toggleJSSamplingProfiler(); + } else { + Toast.makeText(applicationContext, "JS Sampling Profiler was already running, so did not start the sampling profiler", Toast.LENGTH_LONG).show(); + } + } + // code removed by ReactAndroidCodeTransformer + ; + if (!getDevSettings().isDeviceDebugEnabled()) { + // For remote debugging, we open up Chrome running the app in a web worker. + // Note that this requires async communication, which will not work for Turbo Modules. + addCustomDevOption(getDevSettings().isRemoteJSDebugEnabled() ? applicationContext.getString(com.facebook.react.R.string.reactandroid_catalyst_debug_stop) : applicationContext.getString(com.facebook.react.R.string.reactandroid_catalyst_debug), new DevOptionHandler() { + + @Override + public void onOptionSelected() { + getDevSettings().setRemoteJSDebugEnabled(!getDevSettings().isRemoteJSDebugEnabled()); + handleReloadJS(); + } + }); + } + } + + @Override + protected String getUniqueTag() { + return "Bridge"; + } + + @Override + public void loadSplitBundleFromServer(final String bundlePath, final DevSplitBundleCallback callback) { + fetchSplitBundleAndCreateBundleLoader(bundlePath, new CallbackWithBundleLoader() { + + @Override + public void onSuccess(JSBundleLoader bundleLoader) { + bundleLoader.loadScript(getCurrentContext().getCatalystInstance()); + getCurrentContext().getJSModule(HMRClient.class).registerBundle(getDevServerHelper().getDevServerSplitBundleURL(bundlePath)); + callback.onSuccess(); + } + + @Override + public void onError(String url, Throwable cause) { + callback.onError(url, cause); + } + }); + } + + private WebsocketJavaScriptExecutor.JSExecutorConnectCallback getExecutorConnectCallback(final SimpleSettableFuture future) { + return new WebsocketJavaScriptExecutor.JSExecutorConnectCallback() { + + @Override + public void onSuccess() { + future.set(true); + hideDevLoadingView(); + } + + @Override + public void onFailure(final Throwable cause) { + hideDevLoadingView(); + FLog.e(ReactConstants.TAG, "Failed to connect to debugger!", cause); + future.setException(new IOException(getApplicationContext().getString(com.facebook.react.R.string.reactandroid_catalyst_debug_error), cause)); + } + }; + } + + private void reloadJSInProxyMode() { + // When using js proxy, there is no need to fetch JS bundle as proxy executor will do that + // anyway + getDevServerHelper().launchJSDevtools(); + JavaJSExecutor.Factory factory = new JavaJSExecutor.Factory() { + + @Override + public JavaJSExecutor create() throws Exception { + WebsocketJavaScriptExecutor executor = new WebsocketJavaScriptExecutor(); + SimpleSettableFuture future = new SimpleSettableFuture<>(); + executor.connect(getDevServerHelper().getWebsocketProxyURL(), getExecutorConnectCallback(future)); + // TODO(t9349129) Don't use timeout + try { + future.get(90, TimeUnit.SECONDS); + return executor; + } catch (ExecutionException e) { + throw (Exception) e.getCause(); + } catch (InterruptedException | TimeoutException e) { + throw new RuntimeException(e); + } + } + }; + getReactInstanceDevHelper().onReloadWithJSDebugger(factory); + } + + @Override + public void handleReloadJS() { + UiThreadUtil.assertOnUiThread(); + ReactMarker.logMarker(ReactMarkerConstants.RELOAD, getDevSettings().getPackagerConnectionSettings().getDebugServerHost()); + // dismiss redbox if exists + hideRedboxDialog(); + if (getDevSettings().isRemoteJSDebugEnabled()) { + PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Proxy"); + showDevLoadingViewForRemoteJSEnabled(); + reloadJSInProxyMode(); + } else { + PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Server"); + String bundleURL = getDevServerHelper().getDevServerBundleURL(Assertions.assertNotNull(getJSAppBundleName())); + reloadJSFromServer(bundleURL); + } + } + + /** Starts of stops the sampling profiler */ + private void toggleJSSamplingProfiler() { + JavaScriptExecutorFactory javaScriptExecutorFactory = getReactInstanceDevHelper().getJavaScriptExecutorFactory(); + if (!mIsSamplingProfilerEnabled) { + try { + javaScriptExecutorFactory.startSamplingProfiler(); + Toast.makeText(getApplicationContext(), "Starting Sampling Profiler", Toast.LENGTH_SHORT).show(); + } catch (UnsupportedOperationException e) { + Toast.makeText(getApplicationContext(), javaScriptExecutorFactory.toString() + " does not support Sampling Profiler", Toast.LENGTH_LONG).show(); + } finally { + mIsSamplingProfilerEnabled = true; + } + } else { + try { + final String outputPath = File.createTempFile("sampling-profiler-trace", ".cpuprofile", getApplicationContext().getCacheDir()).getPath(); + javaScriptExecutorFactory.stopSamplingProfiler(outputPath); + Toast.makeText(getApplicationContext(), "Saved results from Profiler to " + outputPath, Toast.LENGTH_LONG).show(); + } catch (IOException e) { + FLog.e(ReactConstants.TAG, "Could not create temporary file for saving results from Sampling Profiler"); + } catch (UnsupportedOperationException e) { + Toast.makeText(getApplicationContext(), javaScriptExecutorFactory.toString() + "does not support Sampling Profiler", Toast.LENGTH_LONG).show(); + } finally { + mIsSamplingProfilerEnabled = false; + } + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java new file mode 100644 index 0000000000000..0ef0ca065557f --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.devsupport; + +import android.content.Context; +import androidx.annotation.Nullable; +import com.facebook.react.common.SurfaceDelegateFactory; +import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; +import com.facebook.react.devsupport.interfaces.DevSupportManager; +import com.facebook.react.packagerconnection.RequestHandler; +import java.lang.reflect.Constructor; +import java.util.Map; + +/** + * A simple factory that creates instances of {@link DevSupportManager} implementations. Uses + * reflection to create BridgeDevSupportManager if it exists. This allows ProGuard to strip that + * class and its dependencies in release builds. If the class isn't found, {@link + * DisabledDevSupportManager} is returned instead. + */ +public class DefaultDevSupportManagerFactory implements DevSupportManagerFactory { + + private static final String DEVSUPPORT_IMPL_PACKAGE = "com.facebook.react.devsupport"; + private static final String DEVSUPPORT_IMPL_CLASS = "BridgeDevSupportManager"; + + public DevSupportManager create( + Context applicationContext, + ReactInstanceDevHelper reactInstanceDevHelper, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + int minNumShakes) { + + return create( + applicationContext, + reactInstanceDevHelper, + packagerPathForJSBundleName, + enableOnCreate, + null, + null, + minNumShakes, + null, + null); + } + + @Override + public DevSupportManager create( + Context applicationContext, + ReactInstanceDevHelper reactInstanceManagerHelper, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + @Nullable RedBoxHandler redBoxHandler, + @Nullable DevBundleDownloadListener devBundleDownloadListener, + int minNumShakes, + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { + if (!enableOnCreate) { + return new DisabledDevSupportManager(); + } + try { + // ProGuard is surprisingly smart in this case and will keep a class if it detects a call to + // Class.forName() with a static string. So instead we generate a quasi-dynamic string to + // confuse it. + String className = + new StringBuilder(DEVSUPPORT_IMPL_PACKAGE) + .append(".") + .append(DEVSUPPORT_IMPL_CLASS) + .toString(); + Class devSupportManagerClass = Class.forName(className); + Constructor constructor = + devSupportManagerClass.getConstructor( + Context.class, + ReactInstanceDevHelper.class, + String.class, + boolean.class, + RedBoxHandler.class, + DevBundleDownloadListener.class, + int.class, + Map.class, + SurfaceDelegateFactory.class); + return (DevSupportManager) + constructor.newInstance( + applicationContext, + reactInstanceManagerHelper, + packagerPathForJSBundleName, + true, + redBoxHandler, + devBundleDownloadListener, + minNumShakes, + customPackagerCommandHandlers, + surfaceDelegateFactory); + } catch (Exception e) { + throw new RuntimeException( + "Requested enabled DevSupportManager, but BridgeDevSupportManager class was not found" + + " or could not be created", + e); + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevLoadingViewController.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevLoadingViewController.java index bc51eaf252fe8..687a90ec1f032 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevLoadingViewController.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevLoadingViewController.java @@ -29,7 +29,7 @@ /** Controller to display loading messages on top of the screen. All methods are thread safe. */ public class DevLoadingViewController { private static boolean sEnabled = true; - private final ReactInstanceManagerDevHelper mReactInstanceManagerHelper; + private final ReactInstanceDevHelper mReactInstanceManagerHelper; private @Nullable TextView mDevLoadingView; private @Nullable PopupWindow mDevLoadingPopup; @@ -37,7 +37,7 @@ public static void setDevLoadingEnabled(boolean enabled) { sEnabled = enabled; } - public DevLoadingViewController(ReactInstanceManagerDevHelper reactInstanceManagerHelper) { + public DevLoadingViewController(ReactInstanceDevHelper reactInstanceManagerHelper) { mReactInstanceManagerHelper = reactInstanceManagerHelper; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index fe2dc40b30e44..d18918884be7c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -12,9 +12,10 @@ import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.R; import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.R; +import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; import com.facebook.react.devsupport.interfaces.StackFrame; @@ -41,7 +42,6 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import okhttp3.ResponseBody; import okio.Okio; import okio.Sink; import org.json.JSONArray; @@ -64,8 +64,6 @@ public class DevServerHelper { public static String RELOAD_APP_EXTRA_JS_PROXY = "jsproxy"; - public static String PACKAGER_OK_STATUS = "packager-status:running"; - public static int HTTP_CONNECT_TIMEOUT_MS = 5000; public static String DEBUGGER_MSG_DISABLE = "{ \"id\":1,\"method\":\"Debugger.disable\" }"; @@ -121,6 +119,8 @@ public String typeID() { public final BundleDownloader mBundleDownloader; + public final PackagerStatusCheck mPackagerStatusCheck; + public final String mPackageName; public boolean mPackagerConnectionLock = false; @@ -138,6 +138,7 @@ public DevServerHelper(DevInternalSettings settings, String packageName, Inspect mBundlerStatusProvider = bundleStatusProvider; mClient = new OkHttpClient.Builder().connectTimeout(HTTP_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS).readTimeout(0, TimeUnit.MILLISECONDS).writeTimeout(0, TimeUnit.MILLISECONDS).build(); mBundleDownloader = new BundleDownloader(mClient); + mPackagerStatusCheck = new PackagerStatusCheck(mClient); mPackageName = packageName; } @@ -436,44 +437,13 @@ public String getDevServerSplitBundleURL(String jsModulePath) { } public void isPackagerRunning(final PackagerStatusCallback callback) { - String statusURL = createPackagerStatusURL(mSettings.getPackagerConnectionSettings().getDebugServerHost()); - Request request = new Request.Builder().url(statusURL).build(); - mClient.newCall(request).enqueue(new Callback() { - - @Override - public void onFailure(Call call, IOException e) { - FLog.w(ReactConstants.TAG, "The packager does not seem to be running as we got an IOException requesting " + "its status: " + e.getMessage()); - callback.onPackagerStatusFetched(false); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - if (!response.isSuccessful()) { - FLog.e(ReactConstants.TAG, "Got non-success http code from packager when requesting status: " + response.code()); - callback.onPackagerStatusFetched(false); - return; - } - ResponseBody body = response.body(); - if (body == null) { - FLog.e(ReactConstants.TAG, "Got null body response from packager when requesting status"); - callback.onPackagerStatusFetched(false); - return; - } - String bodyString = // cannot call body.string() twice, stored it into variable. - body.string(); - // https://github.com/square/okhttp/issues/1240#issuecomment-68142603 - if (!PACKAGER_OK_STATUS.equals(bodyString)) { - FLog.e(ReactConstants.TAG, "Got unexpected response from packager when requesting status: " + bodyString); - callback.onPackagerStatusFetched(false); - return; - } - callback.onPackagerStatusFetched(true); - } - }); - } - - private static String createPackagerStatusURL(String host) { - return String.format(Locale.US, "http://%s/status", host); + String host = mSettings.getPackagerConnectionSettings().getDebugServerHost(); + if (host == null) { + FLog.w(ReactConstants.TAG, "No packager host configured."); + callback.onPackagerStatusFetched(false); + } else { + mPackagerStatusCheck.run(host, callback); + } } private String createLaunchJSDevtoolsCommandUrl() { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java index 8d22726acbfd4..fcf2ad1e73e89 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java @@ -16,22 +16,22 @@ import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.Typeface; import android.hardware.SensorManager; import android.util.Pair; +import android.view.Gravity; import android.view.View; import android.widget.EditText; +import android.widget.TextView; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.facebook.common.logging.FLog; -import com.facebook.debug.holder.PrinterHolder; -import com.facebook.debug.tags.ReactDebugOverlayTags; import com.facebook.infer.annotation.Assertions; import com.facebook.react.R; import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler; import com.facebook.react.bridge.JSBundleLoader; -import com.facebook.react.bridge.JavaJSExecutor; -import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; @@ -40,21 +40,21 @@ import com.facebook.react.common.DebugServerException; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ShakeDetector; -import com.facebook.react.common.futures.SimpleSettableFuture; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.DevServerHelper.PackagerCommandListener; +import com.facebook.react.devsupport.interfaces.BundleLoadCallback; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevOptionHandler; -import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.devsupport.interfaces.ErrorCustomizer; +import com.facebook.react.devsupport.interfaces.ErrorType; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; import com.facebook.react.devsupport.interfaces.StackFrame; import com.facebook.react.modules.core.RCTNativeAppEventEmitter; -import com.facebook.react.modules.debug.interfaces.DeveloperSettings; import com.facebook.react.packagerconnection.RequestHandler; import com.facebook.react.packagerconnection.Responder; import java.io.File; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -62,11 +62,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -public abstract class DevSupportManagerBase implements DevSupportManager, PackagerCommandListener, DevInternalSettings.Listener { +public abstract class DevSupportManagerBase implements DevSupportManager { public interface CallbackWithBundleLoader { @@ -79,31 +76,18 @@ public interface CallbackWithBundleLoader { public static int JSEXCEPTION_ERROR_COOKIE = -1; - public static String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js"; - - public static String JS_SPLIT_BUNDLES_DIR_NAME = "dev_js_split_bundles"; - public static String RELOAD_APP_ACTION_SUFFIX = ".RELOAD_APP_ACTION"; public static String FLIPPER_DEBUGGER_URL = "flipper://null/Hermesdebuggerrn?device=React%20Native"; public static String FLIPPER_DEVTOOLS_URL = "flipper://null/React?device=React%20Native"; - public boolean mIsSamplingProfilerEnabled = false; - - private enum ErrorType { - - JS, NATIVE - } - public static String EXOPACKAGE_LOCATION_FORMAT = "/data/local/tmp/exopackage/%s//secondary-dex"; public static String EMOJI_HUNDRED_POINTS_SYMBOL = " 💯"; public static String EMOJI_FACE_WITH_NO_GOOD_GESTURE = " 🙅"; - public final List mExceptionLoggers = new ArrayList<>(); - public final Context mApplicationContext; public final ShakeDetector mShakeDetector; @@ -114,12 +98,12 @@ private enum ErrorType { public final LinkedHashMap mCustomDevOptions = new LinkedHashMap<>(); - public final ReactInstanceManagerDevHelper mReactInstanceManagerHelper; + public final ReactInstanceDevHelper mReactInstanceDevHelper; @Nullable public final String mJSAppBundleName; - public final File mJSBundleTempFile; + public final File mJSBundleDownloadedFile; public final File mJSSplitBundlesDir; @@ -160,6 +144,9 @@ private enum ErrorType { @Nullable public StackFrame[] mLastErrorStack; + @Nullable + public ErrorType mLastErrorType; + public int mLastErrorCookie = 0; @Nullable @@ -176,15 +163,23 @@ private enum ErrorType { @Nullable public Map mCustomPackagerCommandHandlers; - public DevSupportManagerBase(Context applicationContext, ReactInstanceManagerDevHelper reactInstanceManagerHelper, @Nullable String packagerPathForJSBundleName, boolean enableOnCreate, int minNumShakes) { - this(applicationContext, reactInstanceManagerHelper, packagerPathForJSBundleName, enableOnCreate, null, null, minNumShakes, null); - } + @Nullable + public Activity currentActivity; - public DevSupportManagerBase(Context applicationContext, ReactInstanceManagerDevHelper reactInstanceManagerHelper, @Nullable String packagerPathForJSBundleName, boolean enableOnCreate, @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, @Nullable Map customPackagerCommandHandlers) { - mReactInstanceManagerHelper = reactInstanceManagerHelper; + @Nullable + public final SurfaceDelegateFactory mSurfaceDelegateFactory; + + public DevSupportManagerBase(Context applicationContext, ReactInstanceDevHelper reactInstanceDevHelper, @Nullable String packagerPathForJSBundleName, boolean enableOnCreate, @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, @Nullable Map customPackagerCommandHandlers, @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { + mReactInstanceDevHelper = reactInstanceDevHelper; mApplicationContext = applicationContext; mJSAppBundleName = packagerPathForJSBundleName; - mDevSettings = new DevInternalSettings(applicationContext, this); + mDevSettings = new DevInternalSettings(applicationContext, new DevInternalSettings.Listener() { + + @Override + public void onInternalSettingsChanged() { + reloadSettings(); + } + }); mBundleStatus = new InspectorPackagerConnection.BundleStatus(); mDevServerHelper = new DevServerHelper(mDevSettings, mApplicationContext.getPackageName(), new InspectorPackagerConnection.BundleStatusProvider() { @@ -225,30 +220,25 @@ public void onReceive(Context context, Intent intent) { // start reading first reload output while the second reload starts writing to the same // file. As this should only be the case in dev mode we leave it as it is. // TODO(6418010): Fix readers-writers problem in debug reload from HTTP server - mJSBundleTempFile = new File(applicationContext.getFilesDir(), JS_BUNDLE_FILE_NAME); - mJSSplitBundlesDir = mApplicationContext.getDir(JS_SPLIT_BUNDLES_DIR_NAME, Context.MODE_PRIVATE); + final String subclassTag = getUniqueTag(); + final String bundleFile = subclassTag + "ReactNativeDevBundle.js"; + mJSBundleDownloadedFile = new File(applicationContext.getFilesDir(), bundleFile); + final String splitBundlesDir = subclassTag.toLowerCase() + "_dev_js_split_bundles"; + mJSSplitBundlesDir = mApplicationContext.getDir(splitBundlesDir, Context.MODE_PRIVATE); mDefaultNativeModuleCallExceptionHandler = new DefaultNativeModuleCallExceptionHandler(); setDevSupportEnabled(enableOnCreate); mRedBoxHandler = redBoxHandler; - mDevLoadingViewController = new DevLoadingViewController(reactInstanceManagerHelper); - mExceptionLoggers.add(new JSExceptionLogger()); - if (mDevSettings.isStartSamplingProfilerOnInit()) { - // Only start the profiler. If its already running, there is an error - if (!mIsSamplingProfilerEnabled) { - toggleJSSamplingProfiler(); - } else { - Toast.makeText(mApplicationContext, "JS Sampling Profiler was already running, so did not start the sampling profiler", Toast.LENGTH_LONG).show(); - } - } + mDevLoadingViewController = new DevLoadingViewController(reactInstanceDevHelper); + mSurfaceDelegateFactory = surfaceDelegateFactory; } + protected abstract String getUniqueTag(); + @Override public void handleException(Exception e) { try { if (mIsDevSupportEnabled) { - for (ExceptionLogger logger : mExceptionLoggers) { - logger.log(e); - } + logJSException(e); } else { mDefaultNativeModuleCallExceptionHandler.handleException(e); } @@ -261,30 +251,21 @@ public void handleException(Exception e) { } } - private interface ExceptionLogger { - - void log(Exception ex); - } - - private class JSExceptionLogger implements ExceptionLogger { - - @Override - public void log(Exception e) { - StringBuilder message = new StringBuilder(e.getMessage() == null ? "Exception in native call from JS" : e.getMessage()); - Throwable cause = e.getCause(); - while (cause != null) { - message.append("\n\n").append(cause.getMessage()); - cause = cause.getCause(); - } - if (e instanceof JSException) { - FLog.e(ReactConstants.TAG, "Exception in native call from JS", e); - String stack = ((JSException) e).getStack(); - message.append("\n\n").append(stack); - // TODO #11638796: convert the stack into something useful - showNewError(message.toString(), new StackFrame[] {}, JSEXCEPTION_ERROR_COOKIE, ErrorType.JS); - } else { - showNewJavaError(message.toString(), e); - } + private void logJSException(Exception e) { + StringBuilder message = new StringBuilder(e.getMessage() == null ? "Exception in native call from JS" : e.getMessage()); + Throwable cause = e.getCause(); + while (cause != null) { + message.append("\n\n").append(cause.getMessage()); + cause = cause.getCause(); + } + if (e instanceof JSException) { + FLog.e(ReactConstants.TAG, "Exception in native call from JS", e); + String stack = ((JSException) e).getStack(); + message.append("\n\n").append(stack); + // TODO #11638796: convert the stack into something useful + showNewError(message.toString(), new StackFrame[] {}, JSEXCEPTION_ERROR_COOKIE, ErrorType.JS); + } else { + showNewJavaError(message.toString(), e); } } @@ -347,7 +328,7 @@ public void run() { updateLastErrorInfo(message, stack, errorCookie, ErrorType.JS); // JS errors are reported here after source mapping. if (mRedBoxHandler != null) { - mRedBoxHandler.handleRedbox(message, stack, RedBoxHandler.ErrorType.JS); + mRedBoxHandler.handleRedbox(message, stack, ErrorType.JS); mRedBoxDialog.resetReporting(); } mRedBoxDialog.show(); @@ -366,11 +347,11 @@ public void hideRedboxDialog() { @Nullable public View createRootView(String appKey) { - return mReactInstanceManagerHelper.createRootView(appKey); + return mReactInstanceDevHelper.createRootView(appKey); } public void destroyRootView(View rootView) { - mReactInstanceManagerHelper.destroyRootView(rootView); + mReactInstanceDevHelper.destroyRootView(rootView); } private void hideDevOptionsDialog() { @@ -385,13 +366,18 @@ private void showNewError(@Nullable final String message, final StackFrame[] sta @Override public void run() { + Activity context = mReactInstanceDevHelper.getCurrentActivity(); + if (context != null && !context.isFinishing() && currentActivity != context) { + currentActivity = context; + // Create a new RedBox when currentActivity get updated + mRedBoxDialog = new RedBoxDialog(currentActivity, DevSupportManagerBase.this, mRedBoxHandler); + } + if (currentActivity == null || currentActivity.isFinishing()) { + FLog.e(ReactConstants.TAG, "Unable to launch redbox because react activity " + "is not available, here is the error that redbox would've displayed: " + message); + return; + } if (mRedBoxDialog == null) { - Activity context = mReactInstanceManagerHelper.getCurrentActivity(); - if (context == null || context.isFinishing()) { - FLog.e(ReactConstants.TAG, "Unable to launch redbox because react activity " + "is not available, here is the error that redbox would've displayed: " + message); - return; - } - mRedBoxDialog = new RedBoxDialog(context, DevSupportManagerBase.this, mRedBoxHandler); + mRedBoxDialog = new RedBoxDialog(currentActivity, DevSupportManagerBase.this, mRedBoxHandler); } if (mRedBoxDialog.isShowing()) { // show the first and most actionable one. @@ -402,7 +388,7 @@ public void run() { updateLastErrorInfo(message, stack, errorCookie, errorType); // inside {@link #updateJSError} after source mapping. if (mRedBoxHandler != null && errorType == ErrorType.NATIVE) { - mRedBoxHandler.handleRedbox(message, stack, RedBoxHandler.ErrorType.NATIVE); + mRedBoxHandler.handleRedbox(message, stack, ErrorType.NATIVE); } mRedBoxDialog.resetReporting(); mRedBoxDialog.show(); @@ -428,21 +414,9 @@ public void reloadExpoApp() { } } - // @tsapeta This method can be removed once we fully remove support for ExpoKit, - // then our React Native fork will be used only in managed workflow and standalone builds have dev support disabled anyway. - public boolean isExpoStandaloneApp() { - try { - return (boolean) Class.forName("host.exp.exponent.Constants").getMethod("isStandaloneApp").invoke(null); - } catch (Exception e) { - // This shouldn't ever happen, but `false` as a fallback seems to be better than `true`. - FLog.e("Expo", "Unable to find host.exp.exponent.Constants#isStandaloneApp method."); - return false; - } - } - @Override public void showDevOptionsDialog() { - if (mDevOptionsDialog != null || !mIsDevSupportEnabled || ActivityManager.isUserAMonkey() || !isExpoStandaloneApp()) { + if (mDevOptionsDialog != null || !mIsDevSupportEnabled || ActivityManager.isUserAMonkey()) { return; } LinkedHashMap options = new LinkedHashMap<>(); @@ -480,27 +454,15 @@ public void onOptionSelected() { mDevServerHelper.openUrl(mCurrentContext, FLIPPER_DEVTOOLS_URL, mApplicationContext.getString(R.string.reactandroid_catalyst_open_flipper_error)); } }); - } else { - // For remote debugging, we open up Chrome running the app in a web worker. - // Note that this requires async communication, which will not work for Turbo Modules. - options.put(mDevSettings.isRemoteJSDebugEnabled() ? mApplicationContext.getString(R.string.reactandroid_catalyst_debug_stop) : mApplicationContext.getString(R.string.reactandroid_catalyst_debug), new DevOptionHandler() { - - @Override - public void onOptionSelected() { - mDevSettings.setRemoteJSDebugEnabled(!mDevSettings.isRemoteJSDebugEnabled()); - handleReloadJS(); - } - }); } // code removed by ReactAndroidCodeTransformer ; - options.put(// NOTE: `isElementInspectorEnabled` is not guaranteed to be accurate. - mApplicationContext.getString(R.string.reactandroid_catalyst_inspector), new DevOptionHandler() { + options.put(mDevSettings.isElementInspectorEnabled() ? mApplicationContext.getString(R.string.reactandroid_catalyst_inspector_stop) : mApplicationContext.getString(R.string.reactandroid_catalyst_inspector), new DevOptionHandler() { @Override public void onOptionSelected() { mDevSettings.setElementInspectorEnabled(!mDevSettings.isElementInspectorEnabled()); - mReactInstanceManagerHelper.toggleElementInspector(); + mReactInstanceDevHelper.toggleElementInspector(); } }); options.put(mDevSettings.isHotModuleReplacementEnabled() ? mApplicationContext.getString(R.string.reactandroid_catalyst_hot_reloading_stop) : mApplicationContext.getString(R.string.reactandroid_catalyst_hot_reloading), new DevOptionHandler() { @@ -520,15 +482,13 @@ public void onOptionSelected() { ; } }); - // code removed by ReactAndroidCodeTransformer - ; options.put(mDevSettings.isFpsDebugEnabled() ? mApplicationContext.getString(R.string.reactandroid_catalyst_perf_monitor_stop) : mApplicationContext.getString(R.string.reactandroid_catalyst_perf_monitor), new DevOptionHandler() { @Override public void onOptionSelected() { if (!mDevSettings.isFpsDebugEnabled()) { // Request overlay permission if needed when "Show Perf Monitor" option is selected - Context context = mReactInstanceManagerHelper.getCurrentActivity(); + Context context = mReactInstanceDevHelper.getCurrentActivity(); if (context == null) { FLog.e(ReactConstants.TAG, "Unable to get reference to react activity"); } else { @@ -544,12 +504,19 @@ public void onOptionSelected() { options.putAll(mCustomDevOptions); } final DevOptionHandler[] optionHandlers = options.values().toArray(new DevOptionHandler[0]); - Activity context = mReactInstanceManagerHelper.getCurrentActivity(); + Activity context = mReactInstanceDevHelper.getCurrentActivity(); if (context == null || context.isFinishing()) { FLog.e(ReactConstants.TAG, "Unable to launch dev options menu because react activity " + "isn't available"); return; } - mDevOptionsDialog = new AlertDialog.Builder(context).setItems(options.keySet().toArray(new String[0]), new DialogInterface.OnClickListener() { + final TextView textView = new TextView(getApplicationContext()); + textView.setText("React Native DevMenu (" + getUniqueTag() + ")"); + textView.setPadding(0, 50, 0, 0); + textView.setGravity(Gravity.CENTER); + textView.setTextColor(Color.BLACK); + textView.setTextSize(17); + textView.setTypeface(textView.getTypeface(), Typeface.BOLD); + mDevOptionsDialog = new AlertDialog.Builder(context).setCustomTitle(textView).setItems(options.keySet().toArray(new String[0]), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -569,33 +536,6 @@ public void onCancel(DialogInterface dialog) { } } - /** Starts of stops the sampling profiler */ - private void toggleJSSamplingProfiler() { - JavaScriptExecutorFactory javaScriptExecutorFactory = mReactInstanceManagerHelper.getJavaScriptExecutorFactory(); - if (!mIsSamplingProfilerEnabled) { - try { - javaScriptExecutorFactory.startSamplingProfiler(); - Toast.makeText(mApplicationContext, "Starting Sampling Profiler", Toast.LENGTH_SHORT).show(); - } catch (UnsupportedOperationException e) { - Toast.makeText(mApplicationContext, javaScriptExecutorFactory.toString() + " does not support Sampling Profiler", Toast.LENGTH_LONG).show(); - } finally { - mIsSamplingProfilerEnabled = true; - } - } else { - try { - final String outputPath = File.createTempFile("sampling-profiler-trace", ".cpuprofile", mApplicationContext.getCacheDir()).getPath(); - javaScriptExecutorFactory.stopSamplingProfiler(outputPath); - Toast.makeText(mApplicationContext, "Saved results from Profiler to " + outputPath, Toast.LENGTH_LONG).show(); - } catch (IOException e) { - FLog.e(ReactConstants.TAG, "Could not create temporary file for saving results from Sampling Profiler"); - } catch (UnsupportedOperationException e) { - Toast.makeText(mApplicationContext, javaScriptExecutorFactory.toString() + "does not support Sampling Profiler", Toast.LENGTH_LONG).show(); - } finally { - mIsSamplingProfilerEnabled = false; - } - } - } - /** * {@link ReactInstanceDevCommandsHandler} is responsible for enabling/disabling dev support when * a React view is attached/detached or when application state changes (e.g. the application is @@ -613,7 +553,7 @@ public boolean getDevSupportEnabled() { } @Override - public DeveloperSettings getDevSettings() { + public DevInternalSettings getDevSettings() { return mDevSettings; } @@ -654,7 +594,7 @@ public String getJSBundleURLForRemoteDebugging() { @Override public String getDownloadedJSBundleFile() { - return mJSBundleTempFile.getAbsolutePath(); + return mJSBundleDownloadedFile.getAbsolutePath(); } /** @@ -710,45 +650,44 @@ public void run() { } } - public void onInternalSettingsChanged() { - reloadSettings(); + @Nullable + protected ReactContext getCurrentContext() { + return mCurrentContext; } - // NOTE(brentvatne): this is confusingly called the first time the app loads! - @Override - public void handleReloadJS() { - UiThreadUtil.assertOnUiThread(); - ReactMarker.logMarker(ReactMarkerConstants.RELOAD, mDevSettings.getPackagerConnectionSettings().getDebugServerHost()); - // dismiss redbox if exists - hideRedboxDialog(); - if (mDevSettings.isRemoteJSDebugEnabled()) { - PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Proxy"); - mDevLoadingViewController.showForRemoteJSEnabled(); - mDevLoadingViewVisible = true; - reloadJSInProxyMode(); - } else { - PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Server"); - String bundleURL = mDevServerHelper.getDevServerBundleURL(Assertions.assertNotNull(mJSAppBundleName)); - reloadJSFromServer(bundleURL); - } + @Nullable + protected String getJSAppBundleName() { + return mJSAppBundleName; } - @Override - public void loadSplitBundleFromServer(final String bundlePath, final DevSplitBundleCallback callback) { - fetchSplitBundleAndCreateBundleLoader(bundlePath, new CallbackWithBundleLoader() { + protected Context getApplicationContext() { + return mApplicationContext; + } - @Override - public void onSuccess(JSBundleLoader bundleLoader) { - bundleLoader.loadScript(mCurrentContext.getCatalystInstance()); - mCurrentContext.getJSModule(HMRClient.class).registerBundle(mDevServerHelper.getDevServerSplitBundleURL(bundlePath)); - callback.onSuccess(); - } + protected DevServerHelper getDevServerHelper() { + return mDevServerHelper; + } - @Override - public void onError(String url, Throwable cause) { - callback.onError(url, cause); - } - }); + protected ReactInstanceDevHelper getReactInstanceDevHelper() { + return mReactInstanceDevHelper; + } + + @UiThread + private void showDevLoadingViewForUrl(String bundleUrl) { + mDevLoadingViewController.showForUrl(bundleUrl); + mDevLoadingViewVisible = true; + } + + @UiThread + protected void showDevLoadingViewForRemoteJSEnabled() { + mDevLoadingViewController.showForRemoteJSEnabled(); + mDevLoadingViewVisible = true; + } + + @UiThread + protected void hideDevLoadingView() { + mDevLoadingViewController.hide(); + mDevLoadingViewVisible = false; } public void fetchSplitBundleAndCreateBundleLoader(String bundlePath, final CallbackWithBundleLoader callback) { @@ -772,7 +711,7 @@ public void run() { } }); @Nullable ReactContext context = mCurrentContext; - if (context == null || (!context.isBridgeless() && !context.hasActiveCatalystInstance())) { + if (context == null || !context.hasActiveReactInstance()) { return; } JSBundleLoader bundleLoader = JSBundleLoader.createCachedSplitBundleFromNetworkLoader(bundleUrl, bundleFile.getAbsolutePath()); @@ -802,16 +741,14 @@ public void run() { @UiThread private void showSplitBundleDevLoadingView(String bundleUrl) { - mDevLoadingViewController.showForUrl(bundleUrl); - mDevLoadingViewVisible = true; + showDevLoadingViewForUrl(bundleUrl); mPendingJSSplitBundleRequests++; } @UiThread private void hideSplitBundleDevLoadingView() { if (--mPendingJSSplitBundleRequests == 0) { - mDevLoadingViewController.hide(); - mDevLoadingViewVisible = false; + hideDevLoadingView(); } } @@ -849,57 +786,10 @@ public StackFrame[] getLastErrorStack() { return mLastErrorStack; } - @Override - public void onPackagerConnected() { - // No-op - } - - @Override - public void onPackagerDisconnected() { - // No-op - } - - @Override - public void onPackagerReloadCommand() { - // Disable debugger to resume the JsVM & avoid thread locks while reloading - mDevServerHelper.disableDebugger(); - UiThreadUtil.runOnUiThread(new Runnable() { - - @Override - public void run() { - // NOTE(brentvatne): rather than reload just JS we need to reload the entire project from manifest - // handleReloadJS(); - reloadExpoApp(); - } - }); - } - - @Override - public void onPackagerDevMenuCommand() { - UiThreadUtil.runOnUiThread(new Runnable() { - - @Override - public void run() { - showDevOptionsDialog(); - } - }); - } - - @Override - public void onCaptureHeapCommand(final Responder responder) { - UiThreadUtil.runOnUiThread(new Runnable() { - - @Override - public void run() { - handleCaptureHeap(responder); - } - }); - } - @Override @Nullable - public Map customCommandHandlers() { - return mCustomPackagerCommandHandlers; + public ErrorType getLastErrorType() { + return mLastErrorType; } private void handleCaptureHeap(final Responder responder) { @@ -927,51 +817,7 @@ private void updateLastErrorInfo(@Nullable final String message, final StackFram mLastErrorTitle = message; mLastErrorStack = stack; mLastErrorCookie = errorCookie; - } - - private void reloadJSInProxyMode() { - // When using js proxy, there is no need to fetch JS bundle as proxy executor will do that - // anyway - mDevServerHelper.launchJSDevtools(); - JavaJSExecutor.Factory factory = new JavaJSExecutor.Factory() { - - @Override - public JavaJSExecutor create() throws Exception { - WebsocketJavaScriptExecutor executor = new WebsocketJavaScriptExecutor(); - SimpleSettableFuture future = new SimpleSettableFuture<>(); - executor.connect(mDevServerHelper.getWebsocketProxyURL(), getExecutorConnectCallback(future)); - // TODO(t9349129) Don't use timeout - try { - future.get(90, TimeUnit.SECONDS); - return executor; - } catch (ExecutionException e) { - throw (Exception) e.getCause(); - } catch (InterruptedException | TimeoutException e) { - throw new RuntimeException(e); - } - } - }; - mReactInstanceManagerHelper.onReloadWithJSDebugger(factory); - } - - private WebsocketJavaScriptExecutor.JSExecutorConnectCallback getExecutorConnectCallback(final SimpleSettableFuture future) { - return new WebsocketJavaScriptExecutor.JSExecutorConnectCallback() { - - @Override - public void onSuccess() { - future.set(true); - mDevLoadingViewController.hide(); - mDevLoadingViewVisible = false; - } - - @Override - public void onFailure(final Throwable cause) { - mDevLoadingViewController.hide(); - mDevLoadingViewVisible = false; - FLog.e(ReactConstants.TAG, "Failed to connect to debugger!", cause); - future.setException(new IOException(mApplicationContext.getString(R.string.reactandroid_catalyst_debug_error), cause)); - } - }; + mLastErrorType = errorType; } public void reloadJSFromServer(final String bundleURL) { @@ -983,29 +829,22 @@ public void onSuccess() { @Override public void run() { - mReactInstanceManagerHelper.onJSBundleLoadedFromServer(); + mReactInstanceDevHelper.onJSBundleLoadedFromServer(); } }); } }); } - protected interface BundleLoadCallback { - - void onSuccess(); - } - - protected void reloadJSFromServer(final String bundleURL, final BundleLoadCallback callback) { + public void reloadJSFromServer(final String bundleURL, final BundleLoadCallback callback) { ReactMarker.logMarker(ReactMarkerConstants.DOWNLOAD_START); - mDevLoadingViewController.showForUrl(bundleURL); - mDevLoadingViewVisible = true; + showDevLoadingViewForUrl(bundleURL); final BundleDownloader.BundleInfo bundleInfo = new BundleDownloader.BundleInfo(); mDevServerHelper.downloadBundleFromURL(new DevBundleDownloadListener() { @Override public void onSuccess() { - mDevLoadingViewController.hide(); - mDevLoadingViewVisible = false; + hideDevLoadingView(); synchronized (DevSupportManagerBase.this) { mBundleStatus.isLastDownloadSucess = true; mBundleStatus.updateTimestamp = System.currentTimeMillis(); @@ -1027,8 +866,7 @@ public void onProgress(@Nullable final String status, @Nullable final Integer do @Override public void onFailure(final Exception cause) { - mDevLoadingViewController.hide(); - mDevLoadingViewVisible = false; + hideDevLoadingView(); synchronized (DevSupportManagerBase.this) { mBundleStatus.isLastDownloadSucess = false; } @@ -1038,7 +876,7 @@ public void onFailure(final Exception cause) { FLog.e(ReactConstants.TAG, "Unable to download JS bundle", cause); reportBundleLoadingFailure(cause); } - }, mJSBundleTempFile, bundleURL, bundleInfo); + }, mJSBundleDownloadedFile, bundleURL, bundleInfo); } private void reportBundleLoadingFailure(final Exception cause) { @@ -1122,7 +960,7 @@ public void toggleElementInspector() { @Override public void run() { mDevSettings.setElementInspectorEnabled(!mDevSettings.isElementInspectorEnabled()); - mReactInstanceManagerHelper.toggleElementInspector(); + mReactInstanceDevHelper.toggleElementInspector(); } }); } @@ -1152,7 +990,61 @@ private void reload() { if (mDevLoadingViewVisible) { mDevLoadingViewController.showMessage("Reloading..."); } - mDevServerHelper.openPackagerConnection(this.getClass().getSimpleName(), this); + mDevServerHelper.openPackagerConnection(this.getClass().getSimpleName(), new PackagerCommandListener() { + + @Override + public void onPackagerConnected() { + // No-op + } + + @Override + public void onPackagerDisconnected() { + // No-op + } + + @Override + public void onPackagerReloadCommand() { + // Disable debugger to resume the JsVM & avoid thread locks while reloading + mDevServerHelper.disableDebugger(); + UiThreadUtil.runOnUiThread(new Runnable() { + + @Override + public void run() { + // NOTE(brentvatne): rather than reload just JS we need to reload the entire project from manifest + // handleReloadJS(); + reloadExpoApp(); + } + }); + } + + @Override + public void onPackagerDevMenuCommand() { + UiThreadUtil.runOnUiThread(new Runnable() { + + @Override + public void run() { + showDevOptionsDialog(); + } + }); + } + + @Override + public void onCaptureHeapCommand(final Responder responder) { + UiThreadUtil.runOnUiThread(new Runnable() { + + @Override + public void run() { + handleCaptureHeap(responder); + } + }); + } + + @Override + @Nullable + public Map customCommandHandlers() { + return mCustomPackagerCommandHandlers; + } + }); } else { // hide FPS debug overlay if (mDebugOverlayController != null) { @@ -1187,4 +1079,19 @@ private static String getReloadAppAction(Context context) { public void setPackagerLocationCustomizer(DevSupportManager.PackagerLocationCustomizer packagerLocationCustomizer) { mPackagerLocationCustomizer = packagerLocationCustomizer; } + + @Override + @Nullable + public Activity getCurrentActivity() { + return mReactInstanceDevHelper.getCurrentActivity(); + } + + @Override + @Nullable + public SurfaceDelegate createSurfaceDelegate(String moduleName) { + if (mSurfaceDelegateFactory == null) { + return null; + } + return mSurfaceDelegateFactory.createSurfaceDelegate(moduleName); + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java index 9b48b3104d52a..8bdfe8dd1a6ad 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java @@ -9,88 +9,21 @@ import android.content.Context; import androidx.annotation.Nullable; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.packagerconnection.RequestHandler; -import java.lang.reflect.Constructor; import java.util.Map; -/** - * A simple factory that creates instances of {@link DevSupportManager} implementations. Uses - * reflection to create DevSupportManagerImpl if it exists. This allows ProGuard to strip that class - * and its dependencies in release builds. If the class isn't found, {@link - * DisabledDevSupportManager} is returned instead. - */ -public class DevSupportManagerFactory { - - private static final String DEVSUPPORT_IMPL_PACKAGE = "com.facebook.react.devsupport"; - private static final String DEVSUPPORT_IMPL_CLASS = "DevSupportManagerImpl"; - - public static DevSupportManager create( - Context applicationContext, - ReactInstanceManagerDevHelper reactInstanceManagerHelper, - @Nullable String packagerPathForJSBundleName, - boolean enableOnCreate, - int minNumShakes) { - - return create( - applicationContext, - reactInstanceManagerHelper, - packagerPathForJSBundleName, - enableOnCreate, - null, - null, - minNumShakes, - null); - } - - public static DevSupportManager create( +public interface DevSupportManagerFactory { + DevSupportManager create( Context applicationContext, - ReactInstanceManagerDevHelper reactInstanceManagerHelper, + ReactInstanceDevHelper reactInstanceManagerHelper, @Nullable String packagerPathForJSBundleName, boolean enableOnCreate, @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, - @Nullable Map customPackagerCommandHandlers) { - if (!enableOnCreate) { - return new DisabledDevSupportManager(); - } - try { - // ProGuard is surprisingly smart in this case and will keep a class if it detects a call to - // Class.forName() with a static string. So instead we generate a quasi-dynamic string to - // confuse it. - String className = - new StringBuilder(DEVSUPPORT_IMPL_PACKAGE) - .append(".") - .append(DEVSUPPORT_IMPL_CLASS) - .toString(); - Class devSupportManagerClass = Class.forName(className); - Constructor constructor = - devSupportManagerClass.getConstructor( - Context.class, - ReactInstanceManagerDevHelper.class, - String.class, - boolean.class, - RedBoxHandler.class, - DevBundleDownloadListener.class, - int.class, - Map.class); - return (DevSupportManager) - constructor.newInstance( - applicationContext, - reactInstanceManagerHelper, - packagerPathForJSBundleName, - true, - redBoxHandler, - devBundleDownloadListener, - minNumShakes, - customPackagerCommandHandlers); - } catch (Exception e) { - throw new RuntimeException( - "Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found" - + " or could not be created", - e); - } - } + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java deleted file mode 100644 index 8c0beeafe5bd1..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.devsupport; - -import android.content.Context; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.CatalystInstance; -import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; -import com.facebook.react.packagerconnection.RequestHandler; -import java.util.Map; - -/** - * Interface for accessing and interacting with development features. Following features - * are supported through this manager class: - * 1) Displaying JS errors (aka RedBox) - * 2) Displaying developers menu (Reload JS, Debug JS) - * 3) Communication with developer server in order to download updated JS bundle - * 4) Starting/stopping broadcast receiver for js reload signals - * 5) Starting/stopping motion sensor listener that recognize shake gestures which in turn may - * trigger developers menu. - * 6) Launching developers settings view - * - * This class automatically monitors the state of registered views and activities to which they are - * bound to make sure that we don't display overlay or that we we don't listen for sensor events - * when app is backgrounded. - * - * {@link com.facebook.react.ReactInstanceManager} implementation is responsible for instantiating - * this class as well as for populating with a reference to {@link CatalystInstance} whenever - * instance manager recreates it (through {@link #onNewReactContextCreated). Also, instance manager - * is responsible for enabling/disabling dev support in case when app is backgrounded or when all - * the views has been detached from the instance (through {@link #setDevSupportEnabled} method). - * - * IMPORTANT: In order for developer support to work correctly it is required that the - * manifest of your application contain the following entries: - * {@code } - * {@code } - */ -public final class DevSupportManagerImpl extends DevSupportManagerBase { - - public DevSupportManagerImpl( - Context applicationContext, - ReactInstanceManagerDevHelper reactInstanceManagerHelper, - @Nullable String packagerPathForJSBundleName, - boolean enableOnCreate, - int minNumShakes) { - - super( - applicationContext, - reactInstanceManagerHelper, - packagerPathForJSBundleName, - enableOnCreate, - null, - null, - minNumShakes, - null); - } - - public DevSupportManagerImpl( - Context applicationContext, - ReactInstanceManagerDevHelper reactInstanceManagerHelper, - @Nullable String packagerPathForJSBundleName, - boolean enableOnCreate, - @Nullable RedBoxHandler redBoxHandler, - @Nullable DevBundleDownloadListener devBundleDownloadListener, - int minNumShakes, - @Nullable Map customPackagerCommandHandlers) { - super( - applicationContext, - reactInstanceManagerHelper, - packagerPathForJSBundleName, - enableOnCreate, - redBoxHandler, - devBundleDownloadListener, - minNumShakes, - customPackagerCommandHandlers); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java index a5df40e18294a..84530c6e1be43 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java @@ -7,15 +7,19 @@ package com.facebook.react.devsupport; +import android.app.Activity; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.devsupport.interfaces.BundleLoadCallback; import com.facebook.react.devsupport.interfaces.DevOptionHandler; import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.devsupport.interfaces.ErrorCustomizer; +import com.facebook.react.devsupport.interfaces.ErrorType; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; import com.facebook.react.devsupport.interfaces.StackFrame; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; @@ -133,11 +137,16 @@ public void reloadExpoApp() {} @Override public void reloadJSFromServer(String bundleURL) {} + @Override + public void reloadJSFromServer(final String bundleURL, final BundleLoadCallback callback) {} + @Override public void loadSplitBundleFromServer(String bundlePath, DevSplitBundleCallback callback) {} @Override - public void isPackagerRunning(final PackagerStatusCallback callback) {} + public void isPackagerRunning(final PackagerStatusCallback callback) { + callback.onPackagerStatusFetched(false); + } @Override public @Nullable File downloadBundleResourceFromUrlSync( @@ -155,6 +164,11 @@ public void isPackagerRunning(final PackagerStatusCallback callback) {} return null; } + @Override + public @Nullable ErrorType getLastErrorType() { + return null; + } + @Override public void registerErrorCustomizer(ErrorCustomizer errorCustomizer) {} @@ -166,4 +180,14 @@ public void setPackagerLocationCustomizer( public void handleException(Exception e) { mDefaultNativeModuleCallExceptionHandler.handleException(e); } + + @Override + public @Nullable Activity getCurrentActivity() { + return null; + } + + @Override + public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) { + return null; + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java new file mode 100644 index 0000000000000..1a40476c84119 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.devsupport; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.Nullable; +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.devsupport.interfaces.DevSupportManager; +import com.facebook.react.util.RNLog; + +/** + * The implementation of SurfaceDelegate with {@link Activity}. This is the default SurfaceDelegate + * for Mobile. + */ +public class LogBoxDialogSurfaceDelegate implements SurfaceDelegate { + + private @Nullable View mReactRootView; + private @Nullable LogBoxDialog mDialog; + private final DevSupportManager mDevSupportManager; + + LogBoxDialogSurfaceDelegate(DevSupportManager devSupportManager) { + mDevSupportManager = devSupportManager; + } + + @Override + public void createContentView(String appKey) { + Assertions.assertCondition( + appKey.equals("LogBox"), "This surface manager can only create LogBox React application"); + mReactRootView = mDevSupportManager.createRootView("LogBox"); + if (mReactRootView == null) { + RNLog.e("Unable to launch logbox because react was unable to create the root view"); + } + } + + @Override + public boolean isContentViewReady() { + return mReactRootView != null; + } + + @Override + public void destroyContentView() { + if (mReactRootView != null) { + mDevSupportManager.destroyRootView(mReactRootView); + mReactRootView = null; + } + } + + @Override + public void show() { + if (isSurfaceVisible() || !isContentViewReady()) { + return; + } + + final @Nullable Activity context = mDevSupportManager.getCurrentActivity(); + if (context == null || context.isFinishing()) { + RNLog.e( + "Unable to launch logbox because react activity " + + "is not available, here is the error that logbox would've displayed: "); + return; + } + + mDialog = new LogBoxDialog(context, mReactRootView); + mDialog.setCancelable(false); + mDialog.show(); + } + + @Override + public void hide() { + if (!isSurfaceVisible()) { + return; + } + + if (mReactRootView != null && mReactRootView.getParent() != null) { + ((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView); + } + + mDialog.dismiss(); + mDialog = null; + } + + private boolean isSurfaceVisible() { + return mDialog != null; + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java index 0e15efa9738af..1fc7b8b56d6a1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java @@ -7,18 +7,13 @@ package com.facebook.react.devsupport; -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; import androidx.annotation.Nullable; import com.facebook.fbreact.specs.NativeLogBoxSpec; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UiThreadUtil; -import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.SurfaceDelegate; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.util.RNLog; -import com.facebook.common.logging.FLog; @ReactModule(name = LogBoxModule.NAME) public class LogBoxModule extends NativeLogBoxSpec { @@ -26,30 +21,30 @@ public class LogBoxModule extends NativeLogBoxSpec { public static final String NAME = "LogBox"; private final DevSupportManager mDevSupportManager; - private @Nullable View mReactRootView; - private @Nullable LogBoxDialog mLogBoxDialog; + private final SurfaceDelegate mSurfaceDelegate; + /** + * LogBoxModule can be rendered in different surface. By default, it will use LogBoxDialog to wrap + * the content of logs. In other platform (for example VR), a surfaceDelegate can be provided so + * that the content can be wrapped in custom surface. + */ public LogBoxModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) { super(reactContext); mDevSupportManager = devSupportManager; + @Nullable SurfaceDelegate surfaceDelegate = devSupportManager.createSurfaceDelegate(NAME); + if (surfaceDelegate != null) { + mSurfaceDelegate = surfaceDelegate; + } else { + mSurfaceDelegate = new LogBoxDialogSurfaceDelegate(devSupportManager); + } + UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { - if (mReactRootView == null && mDevSupportManager != null) { - try { - mReactRootView = mDevSupportManager.createRootView("LogBox"); - if (mReactRootView == null) { - FLog.e( - ReactConstants.TAG, - "Unable to launch logbox because react was unable to create the root view"); - } - } catch (RuntimeException e) { - RNLog.e("Unable to launch logbox because react was unable to create the root view"); - } - } + mSurfaceDelegate.createContentView("LogBox"); } }); } @@ -61,26 +56,17 @@ public String getName() { @Override public void show() { - if (mReactRootView != null) { - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - if (mLogBoxDialog == null && mReactRootView != null) { - Activity context = getCurrentActivity(); - if (context == null || context.isFinishing()) { - RNLog.e( - "Unable to launch logbox because react activity " - + "is not available, here is the error that logbox would've displayed: "); - return; - } - mLogBoxDialog = new LogBoxDialog(context, mReactRootView); - mLogBoxDialog.setCancelable(false); - mLogBoxDialog.show(); - } - } - }); + if (!mSurfaceDelegate.isContentViewReady()) { + return; } + + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + mSurfaceDelegate.show(); + } + }); } @Override @@ -89,27 +75,18 @@ public void hide() { new Runnable() { @Override public void run() { - if (mLogBoxDialog != null) { - if (mReactRootView != null && mReactRootView.getParent() != null) { - ((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView); - } - mLogBoxDialog.dismiss(); - mLogBoxDialog = null; - } + mSurfaceDelegate.hide(); } }); } @Override - public void onCatalystInstanceDestroy() { + public void invalidate() { UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { - if (mReactRootView != null) { - mDevSupportManager.destroyRootView(mReactRootView); - mReactRootView = null; - } + mSurfaceDelegate.destroyContentView(); } }); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.java new file mode 100644 index 0000000000000..b83ca3ece7f54 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.devsupport; + +import com.facebook.common.logging.FLog; +import com.facebook.react.common.ReactConstants; +import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; +import java.io.IOException; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** Use this class to check if the JavaScript packager is running on the provided host. */ +public class PackagerStatusCheck { + + private static final String PACKAGER_OK_STATUS = "packager-status:running"; + private static final int HTTP_CONNECT_TIMEOUT_MS = 5000; + private static final String PACKAGER_STATUS_URL_TEMPLATE = "http://%s/status"; + + private final OkHttpClient mClient; + + public PackagerStatusCheck() { + mClient = + new OkHttpClient.Builder() + .connectTimeout(HTTP_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .readTimeout(0, TimeUnit.MILLISECONDS) + .writeTimeout(0, TimeUnit.MILLISECONDS) + .build(); + } + + public PackagerStatusCheck(OkHttpClient client) { + mClient = client; + } + + public void run(String host, final PackagerStatusCallback callback) { + String statusURL = createPackagerStatusURL(host); + Request request = new Request.Builder().url(statusURL).build(); + + mClient + .newCall(request) + .enqueue( + new Callback() { + @Override + public void onFailure(Call call, IOException e) { + FLog.w( + ReactConstants.TAG, + "The packager does not seem to be running as we got an IOException requesting " + + "its status: " + + e.getMessage()); + callback.onPackagerStatusFetched(false); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + if (!response.isSuccessful()) { + FLog.e( + ReactConstants.TAG, + "Got non-success http code from packager when requesting status: " + + response.code()); + callback.onPackagerStatusFetched(false); + return; + } + ResponseBody body = response.body(); + if (body == null) { + FLog.e( + ReactConstants.TAG, + "Got null body response from packager when requesting status"); + callback.onPackagerStatusFetched(false); + return; + } + String bodyString = + body.string(); // cannot call body.string() twice, stored it into variable. + // https://github.com/square/okhttp/issues/1240#issuecomment-68142603 + if (!PACKAGER_OK_STATUS.equals(bodyString)) { + FLog.e( + ReactConstants.TAG, + "Got unexpected response from packager when requesting status: " + + bodyString); + callback.onPackagerStatusFetched(false); + return; + } + callback.onPackagerStatusFetched(true); + } + }); + } + + private static String createPackagerStatusURL(String host) { + return String.format(Locale.US, PACKAGER_STATUS_URL_TEMPLATE, host); + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceDevHelper.java similarity index 88% rename from android/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java rename to android/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceDevHelper.java index 02483586b3416..242b0a62fce77 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceDevHelper.java @@ -15,9 +15,10 @@ /** * Interface used by {@link DevSupportManager} for accessing some fields and methods of {@link - * ReactInstanceManager} for the purpose of displaying and handling developer menu options. + * ReactInstanceManager} or {@link ReactHost} for the purpose of displaying and handling developer + * menu options. */ -public interface ReactInstanceManagerDevHelper { +public interface ReactInstanceDevHelper { /** Request react instance recreation with JS debugging enabled. */ void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java index f61b366a77a8a..9791b7fde474a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java @@ -10,29 +10,15 @@ import android.content.Context; import android.text.SpannedString; import androidx.annotation.Nullable; +import com.facebook.react.devsupport.interfaces.ErrorType; import com.facebook.react.devsupport.interfaces.StackFrame; /** - * Interface used by {@link DevSupportManagerImpl} to allow interception on any redboxes during + * Interface used by {@link BridgeDevSupportManager} to allow interception on any redboxes during * development and handling the information from the redbox. The implementation should be passed by * setRedBoxHandler in ReactInstanceManager. */ public interface RedBoxHandler { - enum ErrorType { - JS("JS"), - NATIVE("Native"); - - private final String name; - - ErrorType(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - /** Callback interface for {@link #reportRedbox}. */ interface ReportCompletedListener { void onReportSuccess(SpannedString spannedString); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerMode.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/BundleLoadCallback.java similarity index 58% rename from android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerMode.java rename to android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/BundleLoadCallback.java index 3ed6f114724c8..40495dfdbf934 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerMode.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/BundleLoadCallback.java @@ -5,11 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -package com.facebook.react.modules.datepicker; +package com.facebook.react.devsupport.interfaces; -/** Date picker modes */ -public enum DatePickerMode { - CALENDAR, - SPINNER, - DEFAULT +public interface BundleLoadCallback { + void onSuccess(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java index e3300e84f16b2..35e3c3d1fbdbd 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java @@ -7,17 +7,19 @@ package com.facebook.react.devsupport.interfaces; +import android.app.Activity; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.NativeModuleCallExceptionHandler; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.SurfaceDelegate; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; import java.io.File; /** * Interface for accessing and interacting with development features. In dev mode, use the - * implementation {@link DevSupportManagerImpl}. In production mode, use the dummy implementation + * implementation {@link BridgeDevSupportManager}. In production mode, use the dummy implementation * {@link DisabledDevSupportManager}. */ public interface DevSupportManager extends NativeModuleCallExceptionHandler { @@ -71,6 +73,8 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler { void reloadJSFromServer(final String bundleURL); + void reloadJSFromServer(final String bundleURL, final BundleLoadCallback callback); + void loadSplitBundleFromServer(String bundlePath, DevSplitBundleCallback callback); void isPackagerRunning(PackagerStatusCallback callback); @@ -92,6 +96,9 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler { @Nullable StackFrame[] getLastErrorStack(); + @Nullable + ErrorType getLastErrorType(); + void registerErrorCustomizer(ErrorCustomizer errorCustomizer); /** @@ -104,4 +111,16 @@ public interface PackagerLocationCustomizer { } void setPackagerLocationCustomizer(PackagerLocationCustomizer packagerLocationCustomizer); + + @Nullable + Activity getCurrentActivity(); + + /** + * Create the surface delegate that the provided module should use to interact with + * + * @param moduleName the module name that helps decide which surface it should interact with + * @return a {@link SurfaceDelegate} instance + */ + @Nullable + SurfaceDelegate createSurfaceDelegate(String moduleName); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/ErrorType.java b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/ErrorType.java new file mode 100644 index 0000000000000..2b5a1884b54af --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/ErrorType.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.devsupport.interfaces; + +public enum ErrorType { + JS("JS"), + NATIVE("Native"); + + private final String name; + + ErrorType(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK index a1882a6cdbd5d..df7dee67bd64e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK @@ -8,6 +8,7 @@ rn_android_library( "jsi/*.java", "mounting/**/*.java", ]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -32,11 +33,11 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/config:config"), react_native_target("java/com/facebook/react/fabric/jni:jni"), - react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"), react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/common/mapbuffer:mapbuffer"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/views/view:view"), react_native_target("java/com/facebook/react/views/text:text"), diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java index ea080bd4e071e..6ad92742b864c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java @@ -9,12 +9,15 @@ import android.annotation.SuppressLint; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.NativeMap; +import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.RuntimeExecutor; -import com.facebook.react.bridge.queue.MessageQueueThread; +import com.facebook.react.bridge.RuntimeScheduler; import com.facebook.react.fabric.events.EventBeatManager; +import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.uimanager.PixelUtil; @DoNotStrip @@ -35,9 +38,9 @@ public Binding() { private native void installFabricUIManager( RuntimeExecutor runtimeExecutor, + RuntimeScheduler runtimeScheduler, Object uiManager, EventBeatManager eventBeatManager, - MessageQueueThread jsMessageQueueThread, ComponentFactory componentsRegistry, Object reactNativeConfig); @@ -76,22 +79,25 @@ public native void setConstraints( public native void driveCxxAnimations(); - // TODO (T67721598) Remove the jsContext param once we've migrated to using RuntimeExecutor + public native ReadableNativeMap getInspectorDataForInstance( + EventEmitterWrapper eventEmitterWrapper); + public void register( @NonNull RuntimeExecutor runtimeExecutor, + @Nullable RuntimeScheduler runtimeScheduler, @NonNull FabricUIManager fabricUIManager, @NonNull EventBeatManager eventBeatManager, - @NonNull MessageQueueThread jsMessageQueueThread, @NonNull ComponentFactory componentFactory, @NonNull ReactNativeConfig reactNativeConfig) { fabricUIManager.setBinding(this); installFabricUIManager( runtimeExecutor, + runtimeScheduler, fabricUIManager, eventBeatManager, - jsMessageQueueThread, componentFactory, reactNativeConfig); + setPixelDensity(PixelUtil.getDisplayMetricDensity()); } @@ -100,4 +106,8 @@ public void register( public void unregister() { uninstallFabricUIManager(); } + + public native void registerSurface(SurfaceHandlerBinding surfaceHandler); + + public native void unregisterSurface(SurfaceHandlerBinding surfaceHandler); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricComponents.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricComponents.java index ab16eac8e2785..73d2c6f59bf24 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricComponents.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricComponents.java @@ -14,15 +14,15 @@ /** * Utility class for Fabric components, this will be removed * - *

//TODO T31905686: remove this class when the component names are unified between JS - Android - * - iOS - C++ + *

TODO T97384889: remove this class when the component names are unified between JS - Android - + * iOS - C++ */ public class FabricComponents { private static @NonNull final Map sComponentNames = new HashMap<>(); static { - // TODO T31905686: unify component names between JS - Android - iOS - C++ + // TODO T97384889: unify component names between JS - Android - iOS - C++ sComponentNames.put("View", "RCTView"); sComponentNames.put("Image", "RCTImageView"); sComponentNames.put("ScrollView", "RCTScrollView"); @@ -40,6 +40,7 @@ public class FabricComponents { sComponentNames.put("Map", "RCTMap"); sComponentNames.put("WebView", "RCTWebView"); sComponentNames.put("Keyframes", "RCTKeyframes"); + sComponentNames.put("ImpressionTrackingView", "RCTImpressionTrackingView"); } /** @return the name of component in the Fabric environment */ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java index c079b48231795..811514772d3c2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java @@ -8,35 +8,13 @@ package com.facebook.react.fabric; import androidx.annotation.NonNull; -import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.JSIModuleProvider; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UIManager; -import com.facebook.react.bridge.queue.MessageQueueThread; +import com.facebook.react.common.mapbuffer.ReadableMapBufferSoLoader; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventBeatManager; -import com.facebook.react.fabric.events.EventEmitterWrapper; -import com.facebook.react.fabric.events.FabricEventEmitter; -import com.facebook.react.fabric.mounting.LayoutMetricsConversions; -import com.facebook.react.fabric.mounting.MountingManager; -import com.facebook.react.fabric.mounting.mountitems.BatchMountItem; -import com.facebook.react.fabric.mounting.mountitems.CreateMountItem; -import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem; -import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem; -import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem; -import com.facebook.react.fabric.mounting.mountitems.InsertMountItem; -import com.facebook.react.fabric.mounting.mountitems.MountItem; -import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem; -import com.facebook.react.fabric.mounting.mountitems.RemoveDeleteMultiMountItem; -import com.facebook.react.fabric.mounting.mountitems.SendAccessibilityEvent; -import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdatePaddingMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem; -import com.facebook.react.uimanager.StateWrapper; -import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.events.BatchEventDispatchedListener; -import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.systrace.Systrace; public class FabricJSIModuleProvider implements JSIModuleProvider { @@ -44,91 +22,55 @@ public class FabricJSIModuleProvider implements JSIModuleProvider { @NonNull private final ReactApplicationContext mReactApplicationContext; @NonNull private final ComponentFactory mComponentFactory; @NonNull private final ReactNativeConfig mConfig; + @NonNull private final ViewManagerRegistry mViewManagerRegistry; public FabricJSIModuleProvider( @NonNull ReactApplicationContext reactApplicationContext, @NonNull ComponentFactory componentFactory, - @NonNull ReactNativeConfig config) { + @NonNull ReactNativeConfig config, + @NonNull ViewManagerRegistry viewManagerRegistry) { mReactApplicationContext = reactApplicationContext; mComponentFactory = componentFactory; mConfig = config; + mViewManagerRegistry = viewManagerRegistry; } @Override public UIManager get() { + Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricJSIModuleProvider.get"); final EventBeatManager eventBeatManager = new EventBeatManager(mReactApplicationContext); final FabricUIManager uiManager = createUIManager(eventBeatManager); + Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricJSIModuleProvider.registerBinding"); final Binding binding = new Binding(); - // TODO T31905686: remove this call - loadClasses(); - MessageQueueThread jsMessageQueueThread = - mReactApplicationContext - .getCatalystInstance() - .getReactQueueConfiguration() - .getJSQueueThread(); + + if (ReactFeatureFlags.enableEagerInitializeMapBufferSoFile) { + ReadableMapBufferSoLoader.staticInit(); + } binding.register( mReactApplicationContext.getCatalystInstance().getRuntimeExecutor(), + mReactApplicationContext.getCatalystInstance().getRuntimeScheduler(), uiManager, eventBeatManager, - jsMessageQueueThread, mComponentFactory, mConfig); + + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + return uiManager; } private FabricUIManager createUIManager(@NonNull EventBeatManager eventBeatManager) { Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricJSIModuleProvider.createUIManager"); - UIManagerModule nativeModule = - Assertions.assertNotNull(mReactApplicationContext.getNativeModule(UIManagerModule.class)); - EventDispatcher eventDispatcher = nativeModule.getEventDispatcher(); - FabricUIManager fabricUIManager = - new FabricUIManager( - mReactApplicationContext, - nativeModule.getViewManagerRegistry_DO_NOT_USE(), - eventDispatcher, - eventBeatManager); + FabricUIManager fabricUIManager; + fabricUIManager = + new FabricUIManager(mReactApplicationContext, mViewManagerRegistry, eventBeatManager); Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); return fabricUIManager; } - - // TODO T31905686: eager load Fabric classes, this is temporary and it will be removed - // in the near future - private static void loadClasses() { - EventBeatManager.class.getClass(); - EventEmitterWrapper.class.getClass(); - FabricEventEmitter.class.getClass(); - BatchMountItem.class.getClass(); - CreateMountItem.class.getClass(); - DispatchCommandMountItem.class.getClass(); - DispatchIntCommandMountItem.class.getClass(); - DispatchStringCommandMountItem.class.getClass(); - InsertMountItem.class.getClass(); - MountItem.class.getClass(); - PreAllocateViewMountItem.class.getClass(); - RemoveDeleteMultiMountItem.class.getClass(); - SendAccessibilityEvent.class.getClass(); - UpdateEventEmitterMountItem.class.getClass(); - UpdateLayoutMountItem.class.getClass(); - UpdatePaddingMountItem.class.getClass(); - UpdatePropsMountItem.class.getClass(); - UpdateStateMountItem.class.getClass(); - LayoutMetricsConversions.class.getClass(); - MountingManager.class.getClass(); - Binding.class.getClass(); - ComponentFactory.class.getClass(); - FabricComponents.class.getClass(); - FabricSoLoader.class.getClass(); - FabricUIManager.class.getClass(); - GuardedFrameCallback.class.getClass(); - StateWrapper.class.getClass(); - StateWrapperImpl.class.getClass(); - BatchEventDispatchedListener.class.getClass(); - ReactNativeConfig.class.getClass(); - } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index a61694b67c2fb..270b9466b0d38 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -25,8 +25,8 @@ import android.graphics.Point; import android.os.SystemClock; import android.view.View; +import android.view.accessibility.AccessibilityEvent; import androidx.annotation.AnyThread; -import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; @@ -34,81 +34,77 @@ import com.facebook.debug.holder.PrinterHolder; import com.facebook.debug.tags.ReactDebugOverlayTags; import com.facebook.infer.annotation.ThreadConfined; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.ReactRootView; +import com.facebook.proguard.annotations.DoNotStripAny; +import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReactIgnorableMountingException; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; -import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.RetryableMountingLayerException; import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UIManagerListener; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventBeatManager; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.events.FabricEventEmitter; +import com.facebook.react.fabric.mounting.MountItemDispatcher; import com.facebook.react.fabric.mounting.MountingManager; -import com.facebook.react.fabric.mounting.mountitems.BatchMountItem; -import com.facebook.react.fabric.mounting.mountitems.CreateMountItem; -import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem; +import com.facebook.react.fabric.mounting.SurfaceMountingManager; import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem; import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem; -import com.facebook.react.fabric.mounting.mountitems.InsertMountItem; import com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem; import com.facebook.react.fabric.mounting.mountitems.MountItem; import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem; -import com.facebook.react.fabric.mounting.mountitems.RemoveDeleteMultiMountItem; import com.facebook.react.fabric.mounting.mountitems.SendAccessibilityEvent; -import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdatePaddingMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem; -import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.modules.i18nmanager.I18nUtil; +import com.facebook.react.uimanager.IllegalViewOperationException; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactRoot; import com.facebook.react.uimanager.ReactRootViewTagGenerator; +import com.facebook.react.uimanager.RootViewUtil; import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.ViewManagerPropertyUpdater; import com.facebook.react.uimanager.ViewManagerRegistry; +import com.facebook.react.uimanager.events.EventCategoryDef; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.uimanager.events.EventDispatcherImpl; +import com.facebook.react.uimanager.events.LockFreeEventDispatcherImpl; import com.facebook.react.views.text.TextLayoutManager; -import com.facebook.systrace.Systrace; -import java.util.ArrayDeque; -import java.util.ArrayList; +import com.facebook.react.views.text.TextLayoutManagerMapBuffer; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Queue; import java.util.concurrent.CopyOnWriteArrayList; +/** + * We instruct ProGuard not to strip out any fields or methods, because many of these methods are + * only called through the JNI from Cxx so it appears that most of this class is "unused". + */ @SuppressLint("MissingNativeLoadLibrary") +@DoNotStripAny public class FabricUIManager implements UIManager, LifecycleEventListener { + public static final String TAG = FabricUIManager.class.getSimpleName(); - public static final String TAG = "FabricUIManager"; // The IS_DEVELOPMENT_ENVIRONMENT variable is used to log extra data when running fabric in a // development environment. DO NOT ENABLE THIS ON PRODUCTION OR YOU WILL BE FIRED! - public static final boolean IS_DEVELOPMENT_ENVIRONMENT = false; + public static final boolean IS_DEVELOPMENT_ENVIRONMENT = false && ReactBuildConfig.DEBUG; public static final boolean ENABLE_FABRIC_LOGS = ReactFeatureFlags.enableFabricLogs || PrinterHolder.getPrinter() .shouldDisplayLogMessage(ReactDebugOverlayTags.FABRIC_UI_MANAGER); - private static final int FRAME_TIME_MS = 16; - private static final int MAX_TIME_IN_FRAME_FOR_NON_BATCHED_OPERATIONS_MS = 8; - private static final int PRE_MOUNT_ITEMS_INITIAL_SIZE_ARRAY = 250; static { FabricSoLoader.staticInit(); @@ -118,55 +114,28 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { @NonNull private final ReactApplicationContext mReactApplicationContext; @NonNull private final MountingManager mMountingManager; @NonNull private final EventDispatcher mEventDispatcher; - - @NonNull - private final ConcurrentHashMap mReactContextForRootTag = - new ConcurrentHashMap<>(); + @NonNull private final MountItemDispatcher mMountItemDispatcher; @NonNull private final EventBeatManager mEventBeatManager; - @NonNull private final Object mViewCommandMountItemsLock = new Object(); - @NonNull private final Object mMountItemsLock = new Object(); - @NonNull private final Object mPreMountItemsLock = new Object(); - - private boolean mInDispatch = false; - private int mReDispatchCounter = 0; - - @GuardedBy("mViewCommandMountItemsLock") - @NonNull - private List mViewCommandMountItems = new ArrayList<>(); @NonNull private final CopyOnWriteArrayList mListeners = new CopyOnWriteArrayList<>(); - @GuardedBy("mMountItemsLock") - @NonNull - private List mMountItems = new ArrayList<>(); - - @GuardedBy("mPreMountItemsLock") - @NonNull - private ArrayDeque mPreMountItems = - new ArrayDeque<>(PRE_MOUNT_ITEMS_INITIAL_SIZE_ARRAY); - @ThreadConfined(UI) @NonNull private final DispatchUIFrameCallback mDispatchUIFrameCallback; - @ThreadConfined(UI) - private int mLastExecutedMountItemSurfaceId = -1; - - @ThreadConfined(UI) - private boolean mLastExecutedMountItemSurfaceIdActive = false; - /** * This is used to keep track of whether or not the FabricUIManager has been destroyed. Once the * Catalyst instance is being destroyed, we should cease all operation here. */ private volatile boolean mDestroyed = false; + // TODO T83943316: Delete this variable once StaticViewConfigs are enabled by default + private volatile boolean mShouldDeallocateEventDispatcher = false; + private boolean mDriveCxxAnimations = false; - private long mRunStartTime = 0l; - private long mBatchedExecutionTime = 0l; private long mDispatchViewUpdatesTime = 0l; private long mCommitStartTime = 0l; private long mLayoutTime = 0l; @@ -179,6 +148,19 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { // from C++ to exceed 9,999 and it should be obvious what's going on when analyzing performance. private int mCurrentSynchronousCommitNumber = 10000; + private MountingManager.MountItemExecutor mMountItemExecutor = + new MountingManager.MountItemExecutor() { + @Override + public void executeItems(Queue items) { + // This executor can be technically accessed before the dispatcher is created, + // but if that happens, something is terribly wrong + mMountItemDispatcher.dispatchMountItems(items); + } + }; + + // TODO T83943316: Deprecate and delete this constructor once StaticViewConfigs are enabled by + // default + @Deprecated public FabricUIManager( ReactApplicationContext reactContext, ViewManagerRegistry viewManagerRegistry, @@ -186,8 +168,29 @@ public FabricUIManager( EventBeatManager eventBeatManager) { mDispatchUIFrameCallback = new DispatchUIFrameCallback(reactContext); mReactApplicationContext = reactContext; - mMountingManager = new MountingManager(viewManagerRegistry); + mMountingManager = new MountingManager(viewManagerRegistry, mMountItemExecutor); + mMountItemDispatcher = + new MountItemDispatcher(mMountingManager, new MountItemDispatchListener()); mEventDispatcher = eventDispatcher; + mShouldDeallocateEventDispatcher = false; + mEventBeatManager = eventBeatManager; + mReactApplicationContext.addLifecycleEventListener(this); + } + + public FabricUIManager( + ReactApplicationContext reactContext, + ViewManagerRegistry viewManagerRegistry, + EventBeatManager eventBeatManager) { + mDispatchUIFrameCallback = new DispatchUIFrameCallback(reactContext); + mReactApplicationContext = reactContext; + mMountingManager = new MountingManager(viewManagerRegistry, mMountItemExecutor); + mMountItemDispatcher = + new MountItemDispatcher(mMountingManager, new MountItemDispatchListener()); + mEventDispatcher = + ReactFeatureFlags.enableLockFreeEventDispatcher + ? new LockFreeEventDispatcherImpl(reactContext) + : new EventDispatcherImpl(reactContext); + mShouldDeallocateEventDispatcher = true; mEventBeatManager = eventBeatManager; mReactApplicationContext.addLifecycleEventListener(this); } @@ -196,18 +199,22 @@ public FabricUIManager( @Override @UiThread @ThreadConfined(UI) + @Deprecated public int addRootView( final T rootView, final WritableMap initialProps, final @Nullable String initialUITemplate) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalViewOperationException( + "Do not call addRootView in Fabric; it is unsupported. Call startSurface instead.")); + final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag(); ReactRoot reactRootView = (ReactRoot) rootView; - // TODO T31905686: Combine with startSurface below ThemedReactContext reactContext = new ThemedReactContext( - mReactApplicationContext, rootView.getContext(), reactRootView.getSurfaceID()); - mMountingManager.addRootView(rootTag, rootView); + mReactApplicationContext, rootView.getContext(), reactRootView.getSurfaceID(), rootTag); + mMountingManager.startSurface(rootTag, rootView, reactContext); String moduleName = reactRootView.getJSModuleName(); - mReactContextForRootTag.put(rootTag, reactContext); if (ENABLE_FABRIC_LOGS) { FLog.d(TAG, "Starting surface for module: %s and reactTag: %d", moduleName, rootTag); } @@ -218,6 +225,36 @@ public int addRootView( return rootTag; } + /** + * This API returns metadata associated to the React Component that rendered the Android View + * received as a parameter. + * + * @param surfaceId {@link int} that represents the surfaceId for the View received as a + * parameter. In practice surfaceId can be retrieved calling the {@link View#getId()} method + * on the {@link ReactRoot} that holds the View received as a second parameter. + * @param view {@link View} view that will be used to retrieve the React view hierarchy metadata. + * @return a {@link ReadableMap} that contains metadata associated to the React Component that + * rendered the Android View received as a parameter. For more details about the keys stored + * in the {@link ReadableMap} refer to the "getInspectorDataForInstance" method from + * com/facebook/react/fabric/jni/Binding.cpp file. + */ + @UiThread + @ThreadConfined(UI) + public ReadableMap getInspectorDataForInstance(final int surfaceId, final View view) { + UiThreadUtil.assertOnUiThread(); + int reactTag = view.getId(); + + EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(surfaceId, reactTag); + return mBinding.getInspectorDataForInstance(eventEmitter); + } + + @Override + public void preInitializeViewManagers(List viewManagerNames) { + for (String viewManagerName : viewManagerNames) { + mMountingManager.initializeViewManager(viewManagerName); + } + } + @Override @AnyThread @ThreadConfined(ANY) @@ -230,14 +267,18 @@ public int startSurface( final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag(); Context context = rootView.getContext(); ThemedReactContext reactContext = - new ThemedReactContext(mReactApplicationContext, context, moduleName); + new ThemedReactContext(mReactApplicationContext, context, moduleName, rootTag); if (ENABLE_FABRIC_LOGS) { FLog.d(TAG, "Starting surface for module: %s and reactTag: %d", moduleName, rootTag); } - mMountingManager.addRootView(rootTag, rootView); - mReactContextForRootTag.put(rootTag, reactContext); + mMountingManager.startSurface(rootTag, rootView, reactContext); - Point viewportOffset = ReactRootView.getViewportOffset(rootView); + // If startSurface is executed in the UIThread then, it uses the ViewportOffset from the View, + // Otherwise Fabric relies on calling {@link Binding#setConstraints} method to update the + // ViewportOffset during measurement or onLayout. + @SuppressLint("WrongThread") + Point viewportOffset = + UiThreadUtil.isOnUiThread() ? RootViewUtil.getViewportOffset(rootView) : new Point(0, 0); mBinding.startSurfaceWithConstraints( rootTag, @@ -254,8 +295,57 @@ public int startSurface( return rootTag; } + public void startSurface(final SurfaceHandler surfaceHandler, final @Nullable View rootView) { + final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag(); + + if (rootView == null) { + mMountingManager.startSurface(rootTag); + } else { + Context context = rootView.getContext(); + ThemedReactContext reactContext = + new ThemedReactContext( + mReactApplicationContext, context, surfaceHandler.getModuleName(), rootTag); + mMountingManager.startSurface(rootTag, rootView, reactContext); + } + + surfaceHandler.setSurfaceId(rootTag); + if (surfaceHandler instanceof SurfaceHandlerBinding) { + mBinding.registerSurface((SurfaceHandlerBinding) surfaceHandler); + } + surfaceHandler.setMountable(rootView != null); + surfaceHandler.start(); + } + + public void attachRootView(final SurfaceHandler surfaceHandler, final View rootView) { + ThemedReactContext reactContext = + new ThemedReactContext( + mReactApplicationContext, + rootView.getContext(), + surfaceHandler.getModuleName(), + surfaceHandler.getSurfaceId()); + mMountingManager.attachRootView(surfaceHandler.getSurfaceId(), rootView, reactContext); + + surfaceHandler.setMountable(true); + } + + public void stopSurface(final SurfaceHandler surfaceHandler) { + if (!surfaceHandler.isRunning()) { + ReactSoftExceptionLogger.logSoftException( + FabricUIManager.TAG, + new IllegalStateException("Trying to stop surface that hasn't started yet")); + return; + } + + mMountingManager.stopSurface(surfaceHandler.getSurfaceId()); + + surfaceHandler.stop(); + + if (surfaceHandler instanceof SurfaceHandlerBinding) { + mBinding.unregisterSurface((SurfaceHandlerBinding) surfaceHandler); + } + } + /** Method called when an event has been dispatched on the C++ side. */ - @DoNotStrip @SuppressWarnings("unused") public void onRequestEventBeat() { mEventDispatcher.dispatchAllEvents(); @@ -265,15 +355,13 @@ public void onRequestEventBeat() { @ThreadConfined(ANY) @Override public void stopSurface(final int surfaceID) { - mReactContextForRootTag.remove(surfaceID); + // Mark surfaceId as dead, stop executing mounting instructions + mMountingManager.stopSurface(surfaceID); + + // Communicate stopSurface to Cxx - causes an empty ShadowTree to be committed, + // but all mounting instructions will be ignored because stopSurface was called + // on the MountingManager mBinding.stopSurface(surfaceID); - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - mMountingManager.deleteRootView(surfaceID); - } - }); } @Override @@ -290,7 +378,7 @@ public void onCatalystInstanceDestroy() { FLog.i(TAG, "FabricUIManager.onCatalystInstanceDestroy"); if (mDestroyed) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( FabricUIManager.TAG, new IllegalStateException("Cannot double-destroy FabricUIManager")); return; } @@ -320,148 +408,15 @@ public void onCatalystInstanceDestroy() { mBinding = null; ViewManagerPropertyUpdater.clear(); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private void preallocateView( - int rootTag, - int reactTag, - final String componentName, - @Nullable ReadableMap props, - @Nullable Object stateWrapper, - boolean isLayoutable) { - // This could be null if teardown/navigation away from a surface on the main thread happens - // while a commit is being processed in a different thread. By contract we expect this to be - // possible at teardown, but this race should *never* happen at startup. - @Nullable ThemedReactContext context = mReactContextForRootTag.get(rootTag); - - String component = getFabricComponentName(componentName); - synchronized (mPreMountItemsLock) { - mPreMountItems.add( - new PreAllocateViewMountItem( - context, - rootTag, - reactTag, - component, - props, - (StateWrapper) stateWrapper, - isLayoutable)); + // When using ReactFeatureFlags.enableExperimentalStaticViewConfigs enabled, FabriUIManager is + // responsible for initializing and deallocating EventDispatcher. + // TODO T83943316: Remove this IF once StaticViewConfigs are enabled by default + if (mShouldDeallocateEventDispatcher) { + mEventDispatcher.onCatalystInstanceDestroyed(); } } - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem createMountItem( - String componentName, - @Nullable ReadableMap props, - @Nullable Object stateWrapper, - int reactRootTag, - int reactTag, - boolean isLayoutable) { - String component = getFabricComponentName(componentName); - - // This could be null if teardown/navigation away from a surface on the main thread happens - // while a commit is being processed in a different thread. By contract we expect this to be - // possible at teardown, but this race should *never* happen at startup. - @Nullable ThemedReactContext reactContext = mReactContextForRootTag.get(reactRootTag); - - return new CreateMountItem( - reactContext, - reactRootTag, - reactTag, - component, - props, - (StateWrapper) stateWrapper, - isLayoutable); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem insertMountItem(int reactTag, int parentReactTag, int index) { - return new InsertMountItem(reactTag, parentReactTag, index); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem removeDeleteMultiMountItem(int[] metadata) { - return new RemoveDeleteMultiMountItem(metadata); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem updateLayoutMountItem( - int reactTag, int x, int y, int width, int height, int layoutDirection) { - return new UpdateLayoutMountItem(reactTag, x, y, width, height, layoutDirection); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem updatePaddingMountItem(int reactTag, int left, int top, int right, int bottom) { - return new UpdatePaddingMountItem(reactTag, left, top, right, bottom); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem updatePropsMountItem(int reactTag, ReadableMap map) { - return new UpdatePropsMountItem(reactTag, map); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem updateStateMountItem(int reactTag, @Nullable Object stateWrapper) { - return new UpdateStateMountItem(reactTag, (StateWrapper) stateWrapper); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem updateEventEmitterMountItem(int reactTag, Object eventEmitter) { - return new UpdateEventEmitterMountItem(reactTag, (EventEmitterWrapper) eventEmitter); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem createBatchMountItem( - int rootTag, MountItem[] items, int size, int commitNumber) { - return new BatchMountItem(rootTag, items, size, commitNumber); - } - - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem createIntBufferBatchMountItem( - int rootTag, int[] intBuffer, Object[] objBuffer, int commitNumber) { - // This could be null if teardown/navigation away from a surface on the main thread happens - // while a commit is being processed in a different thread. By contract we expect this to be - // possible at teardown, but this race should *never* happen at startup. - @Nullable ThemedReactContext reactContext = mReactContextForRootTag.get(rootTag); - - return new IntBufferBatchMountItem(rootTag, reactContext, intBuffer, objBuffer, commitNumber); - } - - @DoNotStrip @SuppressWarnings("unused") private NativeArray measureLines( ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { @@ -473,7 +428,20 @@ private NativeArray measureLines( PixelUtil.toPixelFromDIP(width)); } - @DoNotStrip + @SuppressWarnings("unused") + private NativeArray measureLinesMapBuffer( + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, + float width, + float height) { + return (NativeArray) + TextLayoutManagerMapBuffer.measureLines( + mReactApplicationContext, + attributedString, + paragraphAttributes, + PixelUtil.toPixelFromDIP(width)); + } + @SuppressWarnings("unused") private long measure( int rootTag, @@ -498,10 +466,17 @@ private long measure( null); } - @DoNotStrip + @SuppressWarnings("unused") + public int getColor(int surfaceId, ReadableMap platformColor) { + ThemedReactContext context = + mMountingManager.getSurfaceManagerEnforced(surfaceId, "getColor").getContext(); + Integer color = ColorPropConverter.getColor(platformColor, context); + return color != null ? color : 0; + } + @SuppressWarnings("unused") private long measure( - int rootTag, + int surfaceId, String componentName, ReadableMap localData, ReadableMap props, @@ -512,16 +487,16 @@ private long measure( float maxHeight, @Nullable float[] attachmentsPositions) { - // This could be null if teardown/navigation away from a surface on the main thread happens - // while a commit is being processed in a different thread. By contract we expect this to be - // possible at teardown, but this race should *never* happen at startup. - @Nullable - ReactContext context = - rootTag < 0 ? mReactApplicationContext : mReactContextForRootTag.get(rootTag); - - // Don't both measuring if we can't get a context. - if (context == null) { - return 0; + ReactContext context; + if (surfaceId > 0) { + SurfaceMountingManager surfaceMountingManager = + mMountingManager.getSurfaceManagerEnforced(surfaceId, "measure"); + if (surfaceMountingManager.isStopped()) { + return 0; + } + context = surfaceMountingManager.getContext(); + } else { + context = mReactApplicationContext; } return mMountingManager.measure( @@ -537,23 +512,53 @@ private long measure( attachmentsPositions); } + @SuppressWarnings("unused") + private long measureMapBuffer( + int surfaceId, + String componentName, + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, + float minWidth, + float maxWidth, + float minHeight, + float maxHeight, + @Nullable float[] attachmentsPositions) { + + ReactContext context; + if (surfaceId > 0) { + SurfaceMountingManager surfaceMountingManager = + mMountingManager.getSurfaceManagerEnforced(surfaceId, "measure"); + if (surfaceMountingManager.isStopped()) { + return 0; + } + context = surfaceMountingManager.getContext(); + } else { + context = mReactApplicationContext; + } + + // TODO: replace ReadableNativeMap -> ReadableMapBuffer + return mMountingManager.measureTextMapBuffer( + context, + componentName, + attributedString, + paragraphAttributes, + getYogaSize(minWidth, maxWidth), + getYogaMeasureMode(minWidth, maxWidth), + getYogaSize(minHeight, maxHeight), + getYogaMeasureMode(minHeight, maxHeight), + attachmentsPositions); + } + /** - * @param surfaceID {@link int} surface ID + * @param surfaceId {@link int} surface ID * @param defaultTextInputPadding {@link float[]} output parameter will contain the default theme * padding used by RN Android TextInput. * @return if theme data is available in the output parameters. */ - @DoNotStrip - public boolean getThemeData(int surfaceID, float[] defaultTextInputPadding) { - ThemedReactContext themedReactContext = mReactContextForRootTag.get(surfaceID); - if (themedReactContext == null) { - // TODO T68526882: Review if this should cause a crash instead. - ReactSoftException.logSoftException( - TAG, - new ReactNoCrashSoftException( - "Unable to find ThemedReactContext associated to surfaceID: " + surfaceID)); - return false; - } + public boolean getThemeData(int surfaceId, float[] defaultTextInputPadding) { + SurfaceMountingManager surfaceMountingManager = + mMountingManager.getSurfaceManagerEnforced(surfaceId, "getThemeData"); + ThemedReactContext themedReactContext = surfaceMountingManager.getContext(); float[] defaultTextInputPaddingForTheme = UIManagerHelper.getDefaultTextInputPadding(themedReactContext); defaultTextInputPadding[0] = defaultTextInputPaddingForTheme[PADDING_START_INDEX]; @@ -563,6 +568,14 @@ public boolean getThemeData(int surfaceID, float[] defaultTextInputPadding) { return true; } + public void addUIManagerEventListener(UIManagerListener listener) { + mListeners.add(listener); + } + + public void removeUIManagerEventListener(UIManagerListener listener) { + mListeners.remove(listener); + } + @Override @UiThread @ThreadConfined(UI) @@ -572,11 +585,11 @@ public void synchronouslyUpdateViewOnUIThread( int commitNumber = mCurrentSynchronousCommitNumber++; - // We are on the UI thread so this would otherwise be safe to call, *BUT* we don't know - // where we are on the callstack. Why isn't this safe, and why do we have additional safeguards - // here? + // We are on the UI thread so it would otherwise be safe to call `tryDispatchMountItems` here to + // flush previously-queued mountitems, *BUT* we don't know where we are on the callstack. + // Why isn't it safe, and why do we have additional safeguards here? // - // A tangible example where this will cause a crash: + // A tangible example where it would cause a crash, and did in the past: // 1. There are queued "delete" mutations // 2. We're called by this stack trace: // FabricUIManager.synchronouslyUpdateViewOnUIThread(FabricUIManager.java:574) @@ -593,32 +606,43 @@ public void synchronouslyUpdateViewOnUIThread( // android.widget.ScrollView.overScrollBy(ScrollView.java:2040) // android.widget.ScrollView.computeScroll(ScrollView.java:1481) // android.view.View.updateDisplayListIfDirty(View.java:20466) - if (!ReactFeatureFlags.enableDrawMutationFix) { - tryDispatchMountItems(); - } + // 3. A view is deleted while its parent is being drawn, causing a crash. MountItem synchronousMountItem = new MountItem() { @Override public void execute(@NonNull MountingManager mountingManager) { try { - updatePropsMountItem(reactTag, props).execute(mountingManager); + mountingManager.updateProps(reactTag, props); } catch (Exception ex) { - // TODO T42943890: Fix animations in Fabric and remove this try/catch - ReactSoftException.logSoftException( - TAG, - new ReactNoCrashSoftException( - "Caught exception in synchronouslyUpdateViewOnUIThread", ex)); + // TODO T42943890: Fix animations in Fabric and remove this try/catch? + // There might always be race conditions between surface teardown and + // animations/other operations, so it may not be feasible to remove this. + // Practically 100% of reported errors from this point are because the + // surface has stopped by this point, but the MountItem was queued before + // the surface was stopped. It's likely not feasible to prevent all such races. } } + + @Override + public int getSurfaceId() { + return View.NO_ID; + } + + @Override + public String toString() { + String propsString = + IS_DEVELOPMENT_ENVIRONMENT + ? (props != null ? props.toHashMap().toString() : "") + : ""; + return String.format("SYNC UPDATE PROPS [%d]: %s", reactTag, propsString); + } }; // If the reactTag exists, we assume that it might at the end of the next // batch of MountItems. Otherwise, we try to execute immediately. if (!mMountingManager.getViewExists(reactTag)) { - synchronized (mMountItemsLock) { - mMountItems.add(synchronousMountItem); - } + mMountItemDispatcher.addMountItem(synchronousMountItem); return; } @@ -639,20 +663,42 @@ public void execute(@NonNull MountingManager mountingManager) { ReactMarkerConstants.FABRIC_UPDATE_UI_MAIN_THREAD_END, null, commitNumber); } - public void addUIManagerEventListener(UIManagerListener listener) { - mListeners.add(listener); + @SuppressWarnings("unused") + @AnyThread + @ThreadConfined(ANY) + private void preallocateView( + int rootTag, + int reactTag, + final String componentName, + @Nullable ReadableMap props, + @Nullable Object stateWrapper, + @Nullable Object eventEmitterWrapper, + boolean isLayoutable) { + + mMountItemDispatcher.addPreAllocateMountItem( + new PreAllocateViewMountItem( + rootTag, + reactTag, + getFabricComponentName(componentName), + props, + (StateWrapper) stateWrapper, + (EventEmitterWrapper) eventEmitterWrapper, + isLayoutable)); } - public void removeUIManagerEventListener(UIManagerListener listener) { - mListeners.remove(listener); + @SuppressWarnings("unused") + @AnyThread + @ThreadConfined(ANY) + private MountItem createIntBufferBatchMountItem( + int rootTag, int[] intBuffer, Object[] objBuffer, int commitNumber) { + return new IntBufferBatchMountItem(rootTag, intBuffer, objBuffer, commitNumber); } /** * This method enqueues UI operations directly to the UI thread. This might change in the future - * to enforce execution order using {@link ReactChoreographer#CallbackType}. This method should + * to enforce execution order using {@link ReactChoreographer.CallbackType}. This method should * only be called as the result of a new tree being committed. */ - @DoNotStrip @SuppressWarnings("unused") @AnyThread @ThreadConfined(ANY) @@ -669,12 +715,9 @@ private void scheduleMountItem( // When Binding.cpp calls scheduleMountItems during a commit phase, it always calls with // a BatchMountItem. No other sites call into this with a BatchMountItem, and Binding.cpp only // calls scheduleMountItems with a BatchMountItem. - boolean isClassicBatchMountItem = mountItem instanceof BatchMountItem; - boolean isIntBufferMountItem = mountItem instanceof IntBufferBatchMountItem; - boolean isBatchMountItem = isClassicBatchMountItem || isIntBufferMountItem; + boolean isBatchMountItem = mountItem instanceof IntBufferBatchMountItem; boolean shouldSchedule = - (isClassicBatchMountItem && ((BatchMountItem) mountItem).shouldSchedule()) - || (isIntBufferMountItem && ((IntBufferBatchMountItem) mountItem).shouldSchedule()) + (isBatchMountItem && ((IntBufferBatchMountItem) mountItem).shouldSchedule()) || (!isBatchMountItem && mountItem != null); // In case of sync rendering, this could be called on the UI thread. Otherwise, @@ -691,13 +734,11 @@ private void scheduleMountItem( mDispatchViewUpdatesTime = SystemClock.uptimeMillis(); } - if (shouldSchedule && mountItem != null) { - synchronized (mMountItemsLock) { - mMountItems.add(mountItem); - } + if (shouldSchedule) { + mMountItemDispatcher.addMountItem(mountItem); if (UiThreadUtil.isOnUiThread()) { // We only read these flags on the UI thread. - tryDispatchMountItems(); + mMountItemDispatcher.tryDispatchMountItems(); } } @@ -727,306 +768,6 @@ private void scheduleMountItem( } } - @UiThread - @ThreadConfined(UI) - private void tryDispatchMountItems() { - // If we're already dispatching, don't reenter. - // Reentrance can potentially happen a lot on Android in Fabric because - // `updateState` from the - // mounting layer causes mount items to be dispatched synchronously. We want to 1) make sure - // we don't reenter in those cases, but 2) still execute those queued instructions - // synchronously. - // This is a pretty blunt tool, but we might not have better options since we really don't want - // to execute anything out-of-order. - if (mInDispatch) { - return; - } - - final boolean didDispatchItems; - try { - didDispatchItems = dispatchMountItems(); - } catch (Throwable e) { - mReDispatchCounter = 0; - mLastExecutedMountItemSurfaceId = -1; - throw e; - } finally { - // Clean up after running dispatchMountItems - even if an exception was thrown - mInDispatch = false; - } - - for (UIManagerListener listener : mListeners) { - listener.didDispatchMountItems(this); - } - - // Decide if we want to try reentering - if (mReDispatchCounter < 10 && didDispatchItems) { - // Executing twice in a row is normal. Only log after that point. - if (mReDispatchCounter > 2) { - ReactSoftException.logSoftException( - TAG, - new ReactNoCrashSoftException( - "Re-dispatched " - + mReDispatchCounter - + " times. This indicates setState (?) is likely being called too many times during mounting.")); - } - - mReDispatchCounter++; - tryDispatchMountItems(); - } - mReDispatchCounter = 0; - mLastExecutedMountItemSurfaceId = -1; - } - - @UiThread - @ThreadConfined(UI) - private List getAndResetViewCommandMountItems() { - synchronized (mViewCommandMountItemsLock) { - List result = mViewCommandMountItems; - if (result.isEmpty()) { - return null; - } - mViewCommandMountItems = new ArrayList<>(); - return result; - } - } - - @UiThread - @ThreadConfined(UI) - private List getAndResetMountItems() { - synchronized (mMountItemsLock) { - List result = mMountItems; - if (result.isEmpty()) { - return null; - } - mMountItems = new ArrayList<>(); - return result; - } - } - - private ArrayDeque getAndResetPreMountItems() { - synchronized (mPreMountItemsLock) { - ArrayDeque result = mPreMountItems; - if (result.isEmpty()) { - return null; - } - mPreMountItems = new ArrayDeque<>(PRE_MOUNT_ITEMS_INITIAL_SIZE_ARRAY); - return result; - } - } - - /** - * Check if a surfaceId is active and ready for MountItems to be executed against it. It is safe - * and cheap to call this repeatedly because we expect many operations to be batched with the same - * surfaceId in a row and we memoize the parameters and results. - * - * @param surfaceId - * @param context - * @return - */ - @UiThread - @ThreadConfined(UI) - private boolean surfaceActiveForExecution(int surfaceId, String context) { - if (mLastExecutedMountItemSurfaceId != surfaceId) { - mLastExecutedMountItemSurfaceId = surfaceId; - mLastExecutedMountItemSurfaceIdActive = mReactContextForRootTag.get(surfaceId) != null; - - // If there are many MountItems with the same SurfaceId, we only - // log a warning for the first one that is skipped. - if (!mLastExecutedMountItemSurfaceIdActive) { - ReactSoftException.logSoftException( - TAG, - new ReactNoCrashSoftException( - "dispatchMountItems: skipping " - + context - + ", because surface not available: " - + surfaceId)); - } - } - - return mLastExecutedMountItemSurfaceIdActive; - } - - private static void printMountItem(MountItem mountItem, String prefix) { - // If a MountItem description is split across multiple lines, it's because it's a - // compound MountItem. Log each line separately. - String[] mountItemLines = mountItem.toString().split("\n"); - for (String m : mountItemLines) { - FLog.e(TAG, prefix + ": " + m); - } - } - - @UiThread - @ThreadConfined(UI) - /** Nothing should call this directly except for `tryDispatchMountItems`. */ - private boolean dispatchMountItems() { - if (mReDispatchCounter == 0) { - mBatchedExecutionTime = 0; - } - - mRunStartTime = SystemClock.uptimeMillis(); - - List viewCommandMountItemsToDispatch = - getAndResetViewCommandMountItems(); - List mountItemsToDispatch = getAndResetMountItems(); - - if (mountItemsToDispatch == null && viewCommandMountItemsToDispatch == null) { - return false; - } - - // As an optimization, execute all ViewCommands first - // This should be: - // 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always - // wanted to be as "synchronous" as possible. - // 2) Safer: ViewCommands are inherently disconnected from the tree commit/diff/mount process. - // JS imperatively queues these commands. - // If JS has queued a command, it's reasonable to assume that the more time passes, the more - // likely it is that the view disappears. - // Thus, by executing ViewCommands early, we should actually avoid a category of - // errors/glitches. - if (viewCommandMountItemsToDispatch != null) { - Systrace.beginSection( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "FabricUIManager::mountViews viewCommandMountItems to execute: " - + viewCommandMountItemsToDispatch.size()); - for (DispatchCommandMountItem command : viewCommandMountItemsToDispatch) { - if (ENABLE_FABRIC_LOGS) { - printMountItem(command, "dispatchMountItems: Executing viewCommandMountItem"); - } - try { - command.execute(mMountingManager); - } catch (RetryableMountingLayerException e) { - // If the exception is marked as Retryable, we retry the viewcommand exactly once, after - // the current batch of mount items has finished executing. - if (command.getRetries() == 0) { - command.incrementRetries(); - dispatchCommandMountItem(command); - } else { - // It's very common for commands to be executed on views that no longer exist - for - // example, a blur event on TextInput being fired because of a navigation event away - // from the current screen. If the exception is marked as Retryable, we log a soft - // exception but never crash in debug. - // It's not clear that logging this is even useful, because these events are very - // common, mundane, and there's not much we can do about them currently. - ReactSoftException.logSoftException( - TAG, - new ReactNoCrashSoftException( - "Caught exception executing ViewCommand: " + command.toString(), e)); - } - } catch (Throwable e) { - // Non-Retryable exceptions are logged as soft exceptions in prod, but crash in Debug. - ReactSoftException.logSoftException( - TAG, - new RuntimeException( - "Caught exception executing ViewCommand: " + command.toString(), e)); - } - } - - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - } - - // If there are MountItems to dispatch, we make sure all the "pre mount items" are executed - // first - ArrayDeque mPreMountItemsToDispatch = getAndResetPreMountItems(); - - if (mPreMountItemsToDispatch != null) { - Systrace.beginSection( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "FabricUIManager::mountViews preMountItems to execute: " - + mPreMountItemsToDispatch.size()); - - while (!mPreMountItemsToDispatch.isEmpty()) { - PreAllocateViewMountItem mountItem = mPreMountItemsToDispatch.pollFirst(); - if (surfaceActiveForExecution( - mountItem.getRootTag(), "dispatchMountItems PreAllocateViewMountItem")) { - mountItem.execute(mMountingManager); - } - } - - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - } - - if (mountItemsToDispatch != null) { - Systrace.beginSection( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "FabricUIManager::mountViews mountItems to execute: " + mountItemsToDispatch.size()); - - long batchedExecutionStartTime = SystemClock.uptimeMillis(); - - for (MountItem mountItem : mountItemsToDispatch) { - if (ENABLE_FABRIC_LOGS) { - printMountItem(mountItem, "dispatchMountItems: Executing mountItem"); - } - - try { - // Make sure surface associated with this MountItem has been started, and not stopped. - // TODO T68118357: clean up this logic and simplify this method overall - if (mountItem instanceof BatchMountItem) { - BatchMountItem batchMountItem = (BatchMountItem) mountItem; - if (!surfaceActiveForExecution( - batchMountItem.getRootTag(), "dispatchMountItems BatchMountItem")) { - continue; - } - } - - mountItem.execute(mMountingManager); - } catch (Throwable e) { - // If there's an exception, we want to log diagnostics in prod and rethrow. - FLog.e(TAG, "dispatchMountItems: caught exception, displaying all MountItems", e); - for (MountItem m : mountItemsToDispatch) { - printMountItem(m, "dispatchMountItems: mountItem"); - } - - if (ReactIgnorableMountingException.isIgnorable(e)) { - ReactSoftException.logSoftException(TAG, e); - } else { - throw e; - } - } - } - mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime; - } - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - - return true; - } - - @UiThread - @ThreadConfined(UI) - private void dispatchPreMountItems(long frameTimeNanos) { - Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager::premountViews"); - - // dispatchPreMountItems cannot be reentrant, but we want to prevent dispatchMountItems from - // reentering during dispatchPreMountItems - mInDispatch = true; - - try { - while (true) { - long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000); - if (timeLeftInFrame < MAX_TIME_IN_FRAME_FOR_NON_BATCHED_OPERATIONS_MS) { - break; - } - - PreAllocateViewMountItem preMountItemToDispatch; - synchronized (mPreMountItemsLock) { - if (mPreMountItems.isEmpty()) { - break; - } - preMountItemToDispatch = mPreMountItems.pollFirst(); - } - - if (surfaceActiveForExecution( - preMountItemToDispatch.getRootTag(), "dispatchPreMountItems")) { - preMountItemToDispatch.execute(mMountingManager); - } - } - } finally { - mInDispatch = false; - mLastExecutedMountItemSurfaceId = -1; - } - - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - } - public void setBinding(Binding binding) { mBinding = binding; } @@ -1038,17 +779,28 @@ public void setBinding(Binding binding) { @UiThread @ThreadConfined(UI) public void updateRootLayoutSpecs( - final int rootTag, + final int surfaceId, final int widthMeasureSpec, final int heightMeasureSpec, final int offsetX, final int offsetY) { if (ENABLE_FABRIC_LOGS) { - FLog.d(TAG, "Updating Root Layout Specs"); + FLog.d(TAG, "Updating Root Layout Specs for [%d]", surfaceId); } - ThemedReactContext reactContext = mReactContextForRootTag.get(rootTag); + SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId); + + // TODO T83615646: make this a hard-crash in the future. + if (surfaceMountingManager == null) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalViewOperationException( + "Cannot updateRootLayoutSpecs on surfaceId that does not exist: " + surfaceId)); + return; + } + + ThemedReactContext reactContext = surfaceMountingManager.getContext(); boolean isRTL = false; boolean doLeftAndRightSwapInRTL = false; if (reactContext != null) { @@ -1057,7 +809,7 @@ public void updateRootLayoutSpecs( } mBinding.setConstraints( - rootTag, + surfaceId, getMinSize(widthMeasureSpec), getMaxSize(widthMeasureSpec), getMinSize(heightMeasureSpec), @@ -1068,16 +820,87 @@ public void updateRootLayoutSpecs( doLeftAndRightSwapInRTL); } + @Override + public View resolveView(int reactTag) { + UiThreadUtil.assertOnUiThread(); + + SurfaceMountingManager surfaceManager = mMountingManager.getSurfaceManagerForView(reactTag); + return surfaceManager == null ? null : surfaceManager.getView(reactTag); + } + @Override public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap params) { - EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(reactTag); + receiveEvent(View.NO_ID, reactTag, eventName, params); + } + + @Override + public void receiveEvent( + int surfaceId, int reactTag, String eventName, @Nullable WritableMap params) { + receiveEvent(surfaceId, reactTag, eventName, false, 0, params); + } + + public void receiveEvent( + int surfaceId, + int reactTag, + String eventName, + boolean canCoalesceEvent, + int customCoalesceKey, + @Nullable WritableMap params) { + receiveEvent( + surfaceId, + reactTag, + eventName, + canCoalesceEvent, + customCoalesceKey, + params, + EventCategoryDef.UNSPECIFIED); + } + + /** + * receiveEvent API that emits an event to C++. If `canCoalesceEvent` is true, that signals that + * C++ may coalesce the event optionally. Otherwise, coalescing can happen in Java before + * emitting. + * + *

`customCoalesceKey` is currently unused. + * + * @param surfaceId + * @param reactTag + * @param eventName + * @param canCoalesceEvent + * @param customCoalesceKey + * @param params + * @param eventCategory + */ + public void receiveEvent( + int surfaceId, + int reactTag, + String eventName, + boolean canCoalesceEvent, + int customCoalesceKey, + @Nullable WritableMap params, + @EventCategoryDef int eventCategory) { + if (ReactBuildConfig.DEBUG && surfaceId == View.NO_ID) { + FLog.d(TAG, "Emitted event without surfaceId: [%d] %s", reactTag, eventName); + } + + if (mDestroyed) { + FLog.e(TAG, "Attempted to receiveEvent after destruction"); + return; + } + + EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(surfaceId, reactTag); + if (eventEmitter == null) { // This can happen if the view has disappeared from the screen (because of async events) FLog.d(TAG, "Unable to invoke event: " + eventName + " for reactTag: " + reactTag); return; } - eventEmitter.invoke(eventName, params); + if (canCoalesceEvent) { + eventEmitter.invokeUnique(eventName, params, customCoalesceKey); + } else { + eventEmitter.invoke(eventName, params, eventCategory); + } } @Override @@ -1102,38 +925,73 @@ public void onHostPause() { @Override public void onHostDestroy() {} - @Deprecated @Override + @Deprecated @AnyThread @ThreadConfined(ANY) public void dispatchCommand( final int reactTag, final int commandId, @Nullable final ReadableArray commandArgs) { - dispatchCommandMountItem(new DispatchIntCommandMountItem(reactTag, commandId, commandArgs)); + throw new UnsupportedOperationException( + "dispatchCommand called without surfaceId - Fabric dispatchCommand must be called through Fabric JSI API"); } @Override + @Deprecated @AnyThread @ThreadConfined(ANY) public void dispatchCommand( final int reactTag, final String commandId, @Nullable final ReadableArray commandArgs) { - dispatchCommandMountItem(new DispatchStringCommandMountItem(reactTag, commandId, commandArgs)); + throw new UnsupportedOperationException( + "dispatchCommand called without surfaceId - Fabric dispatchCommand must be called through Fabric JSI API"); } + @Deprecated @AnyThread @ThreadConfined(ANY) - private void dispatchCommandMountItem(DispatchCommandMountItem command) { - synchronized (mViewCommandMountItemsLock) { - mViewCommandMountItems.add(command); - } + public void dispatchCommand( + final int surfaceId, + final int reactTag, + final int commandId, + @Nullable final ReadableArray commandArgs) { + mMountItemDispatcher.dispatchCommandMountItem( + new DispatchIntCommandMountItem(surfaceId, reactTag, commandId, commandArgs)); + } + + @AnyThread + @ThreadConfined(ANY) + public void dispatchCommand( + final int surfaceId, + final int reactTag, + final String commandId, + @Nullable final ReadableArray commandArgs) { + mMountItemDispatcher.dispatchCommandMountItem( + new DispatchStringCommandMountItem(surfaceId, reactTag, commandId, commandArgs)); } @Override @AnyThread @ThreadConfined(ANY) public void sendAccessibilityEvent(int reactTag, int eventType) { - synchronized (mMountItemsLock) { - mMountItems.add(new SendAccessibilityEvent(reactTag, eventType)); + // Can be called from native, not just JS - we need to migrate the native callsites + // before removing this entirely. + mMountItemDispatcher.addMountItem(new SendAccessibilityEvent(View.NO_ID, reactTag, eventType)); + } + + @AnyThread + @ThreadConfined(ANY) + public void sendAccessibilityEventFromJS(int surfaceId, int reactTag, String eventTypeJS) { + int eventType; + if ("focus".equals(eventTypeJS)) { + eventType = AccessibilityEvent.TYPE_VIEW_FOCUSED; + } else if ("windowStateChange".equals(eventTypeJS)) { + eventType = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; + } else if ("click".equals(eventTypeJS)) { + eventType = AccessibilityEvent.TYPE_VIEW_CLICKED; + } else { + throw new IllegalArgumentException( + "sendAccessibilityEventFromJS: invalid eventType " + eventTypeJS); } + mMountItemDispatcher.addMountItem(new SendAccessibilityEvent(surfaceId, reactTag, eventType)); } /** @@ -1143,35 +1001,60 @@ public void sendAccessibilityEvent(int reactTag, int eventType) { * @param initialReactTag React tag of the JS view that initiated the touch operation * @param blockNativeResponder If native responder should be blocked or not */ - @DoNotStrip public void setJSResponder( - final int reactTag, final int initialReactTag, final boolean blockNativeResponder) { - synchronized (mMountItemsLock) { - mMountItems.add( - new MountItem() { - @Override - public void execute(MountingManager mountingManager) { - mountingManager.setJSResponder(reactTag, initialReactTag, blockNativeResponder); + final int surfaceId, + final int reactTag, + final int initialReactTag, + final boolean blockNativeResponder) { + mMountItemDispatcher.addMountItem( + new MountItem() { + @Override + public void execute(MountingManager mountingManager) { + SurfaceMountingManager surfaceMountingManager = + mountingManager.getSurfaceManager(surfaceId); + if (surfaceMountingManager != null) { + surfaceMountingManager.setJSResponder( + reactTag, initialReactTag, blockNativeResponder); + } else { + FLog.e( + TAG, "setJSResponder skipped, surface no longer available [" + surfaceId + "]"); } - }); - } + } + + @Override + public int getSurfaceId() { + return surfaceId; + } + + @Override + public String toString() { + return String.format("SET_JS_RESPONDER [%d] [surface:%d]", reactTag, surfaceId); + } + }); } /** - * Clears the JS Responder specified by {@link #setJSResponder(int, int, boolean)}. After this - * method is called, all the touch events are going to be handled by JS. + * Clears the JS Responder specified by {@link #setJSResponder}. After this method is called, all + * the touch events are going to be handled by JS. */ - @DoNotStrip public void clearJSResponder() { - synchronized (mMountItemsLock) { - mMountItems.add( - new MountItem() { - @Override - public void execute(MountingManager mountingManager) { - mountingManager.clearJSResponder(); - } - }); - } + mMountItemDispatcher.addMountItem( + new MountItem() { + @Override + public void execute(MountingManager mountingManager) { + mountingManager.clearJSResponder(); + } + + @Override + public int getSurfaceId() { + return View.NO_ID; + } + + @Override + public String toString() { + return "CLEAR_JS_RESPONDER"; + } + }); } @Override @@ -1186,21 +1069,19 @@ public String resolveCustomDirectEventName(@Nullable String eventName) { if (eventName == null) { return null; } - if (eventName.substring(0, 3).equals("top")) { + if (eventName.startsWith("top")) { return "on" + eventName.substring(3); } return eventName; } // Called from Binding.cpp - @DoNotStrip @AnyThread public void onAnimationStarted() { mDriveCxxAnimations = true; } // Called from Binding.cpp - @DoNotStrip @AnyThread public void onAllAnimationsComplete() { mDriveCxxAnimations = false; @@ -1212,13 +1093,22 @@ public Map getPerformanceCounters() { performanceCounters.put("CommitStartTime", mCommitStartTime); performanceCounters.put("LayoutTime", mLayoutTime); performanceCounters.put("DispatchViewUpdatesTime", mDispatchViewUpdatesTime); - performanceCounters.put("RunStartTime", mRunStartTime); - performanceCounters.put("BatchedExecutionTime", mBatchedExecutionTime); + performanceCounters.put("RunStartTime", mMountItemDispatcher.getRunStartTime()); + performanceCounters.put("BatchedExecutionTime", mMountItemDispatcher.getBatchedExecutionTime()); performanceCounters.put("FinishFabricTransactionTime", mFinishTransactionTime); performanceCounters.put("FinishFabricTransactionCPPTime", mFinishTransactionCPPTime); return performanceCounters; } + private class MountItemDispatchListener implements MountItemDispatcher.ItemDispatchListener { + @Override + public void didDispatchMountItems() { + for (UIManagerListener listener : mListeners) { + listener.didDispatchMountItems(FabricUIManager.this); + } + } + } + private class DispatchUIFrameCallback extends GuardedFrameCallback { private volatile boolean mIsMountingEnabled = true; @@ -1250,9 +1140,8 @@ public void doFrameGuarded(long frameTimeNanos) { } try { - dispatchPreMountItems(frameTimeNanos); - - tryDispatchMountItems(); + mMountItemDispatcher.dispatchPreMountItems(frameTimeNanos); + mMountItemDispatcher.tryDispatchMountItems(); } catch (Exception ex) { FLog.e(TAG, "Exception thrown when executing UIFrameGuarded", ex); stop(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/StateWrapperImpl.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/StateWrapperImpl.java index 6a461623e99ac..821dff8a5fdfb 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/StateWrapperImpl.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/StateWrapperImpl.java @@ -8,14 +8,15 @@ package com.facebook.react.fabric; import android.annotation.SuppressLint; -import androidx.annotation.AnyThread; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.ReadableNativeMap; -import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.uimanager.StateWrapper; /** @@ -28,10 +29,10 @@ public class StateWrapperImpl implements StateWrapper { FabricSoLoader.staticInit(); } - @DoNotStrip private final HybridData mHybridData; + private static final String TAG = "StateWrapperImpl"; - private Runnable mFailureCallback = null; - private int mUpdateStateId = 0; + @DoNotStrip private final HybridData mHybridData; + private volatile boolean mDestroyed = false; private static native HybridData initHybrid(); @@ -39,34 +40,46 @@ private StateWrapperImpl() { mHybridData = initHybrid(); } - @Override - public native ReadableNativeMap getState(); + private native ReadableNativeMap getStateDataImpl(); - public native void updateStateImpl(@NonNull NativeMap map); + private native ReadableMapBuffer getStateMapBufferDataImpl(); - public native void updateStateWithFailureCallbackImpl( - @NonNull NativeMap map, Object self, int updateStateId); + @Override + @Nullable + public ReadableMapBuffer getStatDataMapBuffer() { + if (mDestroyed) { + FLog.e(TAG, "Race between StateWrapperImpl destruction and getState"); + return null; + } + return getStateMapBufferDataImpl(); + } @Override - public void updateState(@NonNull WritableMap map, Runnable failureCallback) { - mUpdateStateId++; - mFailureCallback = failureCallback; - updateStateWithFailureCallbackImpl((NativeMap) map, this, mUpdateStateId); + @Nullable + public ReadableNativeMap getStateData() { + if (mDestroyed) { + FLog.e(TAG, "Race between StateWrapperImpl destruction and getState"); + return null; + } + return getStateDataImpl(); } - @DoNotStrip - @AnyThread - public void updateStateFailed(int callbackRefId) { - // If the callback ref ID doesn't match the ID of the most-recent updateState call, - // then it's an outdated failure callback and we ignore it. - if (callbackRefId != mUpdateStateId) { + public native void updateStateImpl(@NonNull NativeMap map); + + @Override + public void updateState(@NonNull WritableMap map) { + if (mDestroyed) { + FLog.e(TAG, "Race between StateWrapperImpl destruction and updateState"); return; } + updateStateImpl((NativeMap) map); + } - final Runnable failureCallback = mFailureCallback; - mFailureCallback = null; - if (failureCallback != null) { - UiThreadUtil.runOnUiThread(failureCallback); + @Override + public void destroyState() { + if (!mDestroyed) { + mDestroyed = true; + mHybridData.resetNative(); } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java new file mode 100644 index 0000000000000..8a2a482c379af --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.fabric; + +import com.facebook.react.bridge.NativeMap; +import javax.annotation.concurrent.ThreadSafe; + +/** Represents a Java variant of the surface, its status and inner data required to display it. */ +@ThreadSafe +public interface SurfaceHandler { + + /** Starts the surface if the surface is not running */ + void start(); + + /** Stops the surface if it is currently running */ + void stop(); + + void setProps(NativeMap props); + + /** + * Provides current surface id. Id should be updated after each call to {@link + * SurfaceHandler#stop} + */ + int getSurfaceId(); + + /** + * Updates current surface id. Id should be updated after each call to {@link SurfaceHandler#stop} + */ + void setSurfaceId(int surfaceId); + + boolean isRunning(); + + String getModuleName(); + + void setLayoutConstraints( + int widthMeasureSpec, + int heightMeasureSpec, + int offsetX, + int offsetY, + boolean doLeftAndRightSwapInRTL, + boolean isRTL, + float pixelDensity); + + void setMountable(boolean mountable); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java new file mode 100644 index 0000000000000..2683c5746db3b --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/SurfaceHandlerBinding.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.fabric; + +import static com.facebook.react.fabric.mounting.LayoutMetricsConversions.getMaxSize; +import static com.facebook.react.fabric.mounting.LayoutMetricsConversions.getMinSize; + +import androidx.annotation.IntDef; +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.NativeMap; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class SurfaceHandlerBinding implements SurfaceHandler { + static { + FabricSoLoader.staticInit(); + } + + private static final int NO_SURFACE_ID = 0; + + /* Keep in sync with SurfaceHandler.cpp */ + public static final int DISPLAY_MODE_VISIBLE = 0; + public static final int DISPLAY_MODE_SUSPENDED = 1; + public static final int DISPLAY_MODE_HIDDEN = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({DISPLAY_MODE_VISIBLE, DISPLAY_MODE_SUSPENDED, DISPLAY_MODE_HIDDEN}) + public @interface DisplayModeTypeDef {} + + @DoNotStrip private final HybridData mHybridData; + + private static native HybridData initHybrid(int surfaceId, String moduleName); + + public SurfaceHandlerBinding(String moduleName) { + mHybridData = initHybrid(NO_SURFACE_ID, moduleName); + } + + @Override + public int getSurfaceId() { + return getSurfaceIdNative(); + } + + private native int getSurfaceIdNative(); + + @Override + public void setSurfaceId(int surfaceId) { + setSurfaceIdNative(surfaceId); + } + + private native void setSurfaceIdNative(int surfaceId); + + @Override + public String getModuleName() { + return getModuleNameNative(); + } + + private native String getModuleNameNative(); + + @Override + public void start() { + startNative(); + } + + private native void startNative(); + + @Override + public void stop() { + stopNative(); + } + + private native void stopNative(); + + @Override + public boolean isRunning() { + return isRunningNative(); + } + + private native boolean isRunningNative(); + + @Override + public void setLayoutConstraints( + int widthMeasureSpec, + int heightMeasureSpec, + int offsetX, + int offsetY, + boolean doLeftAndRightSwapInRTL, + boolean isRTL, + float pixelDensity) { + setLayoutConstraintsNative( + getMinSize(widthMeasureSpec) / pixelDensity, + getMaxSize(widthMeasureSpec) / pixelDensity, + getMinSize(heightMeasureSpec) / pixelDensity, + getMaxSize(heightMeasureSpec) / pixelDensity, + offsetX / pixelDensity, + offsetY / pixelDensity, + doLeftAndRightSwapInRTL, + isRTL, + pixelDensity); + } + + private native void setLayoutConstraintsNative( + float minWidth, + float maxWidth, + float minHeight, + float maxHeight, + float offsetX, + float offsetY, + boolean doLeftAndRightSwapInRTL, + boolean isRTL, + float pixelDensity); + + @Override + public void setProps(NativeMap props) { + setPropsNative(props); + } + + private native void setPropsNative(NativeMap props); + + @Override + public void setMountable(boolean mountable) { + setDisplayModeNative(mountable ? DISPLAY_MODE_VISIBLE : DISPLAY_MODE_SUSPENDED); + } + + private native void setDisplayModeNative(@DisplayModeTypeDef int mode); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java index a1cd86518c40e..24d33be42f32c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java @@ -16,6 +16,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.fabric.FabricSoLoader; +import com.facebook.react.uimanager.events.EventCategoryDef; /** * This class holds reference to the C++ EventEmitter object. Instances of this class are created on @@ -36,7 +37,11 @@ private EventEmitterWrapper() { mHybridData = initHybrid(); } - private native void invokeEvent(@NonNull String eventName, @NonNull NativeMap params); + private native void invokeEvent( + @NonNull String eventName, @NonNull NativeMap params, @EventCategoryDef int category); + + private native void invokeUniqueEvent( + @NonNull String eventName, @NonNull NativeMap params, int customCoalesceKey); /** * Invokes the execution of the C++ EventEmitter. @@ -44,8 +49,43 @@ private EventEmitterWrapper() { * @param eventName {@link String} name of the event to execute. * @param params {@link WritableMap} payload of the event */ - public void invoke(@NonNull String eventName, @Nullable WritableMap params) { + public synchronized void invoke( + @NonNull String eventName, + @Nullable WritableMap params, + @EventCategoryDef int eventCategory) { + if (!isValid()) { + return; + } NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params; - invokeEvent(eventName, payload); + invokeEvent(eventName, payload, eventCategory); + } + + /** + * Invokes the execution of the C++ EventEmitter. C++ will coalesce events sent to the same + * target. + * + * @param eventName {@link String} name of the event to execute. + * @param params {@link WritableMap} payload of the event + */ + public synchronized void invokeUnique( + @NonNull String eventName, @Nullable WritableMap params, int customCoalesceKey) { + if (!isValid()) { + return; + } + NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params; + invokeUniqueEvent(eventName, payload, customCoalesceKey); + } + + public synchronized void destroy() { + if (mHybridData != null) { + mHybridData.resetNative(); + } + } + + private boolean isValid() { + if (mHybridData != null) { + return mHybridData.isValid(); + } + return false; } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java index 6b9578b442c1d..983466ab3823a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java @@ -9,8 +9,7 @@ import static com.facebook.react.uimanager.events.TouchesHelper.CHANGED_TOUCHES_KEY; import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY; -import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_CANCEL_KEY; -import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_END_KEY; +import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_SURFACE_KEY; import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY; import android.util.Pair; @@ -23,12 +22,14 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.fabric.FabricUIManager; -import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.facebook.react.uimanager.events.EventCategoryDef; +import com.facebook.react.uimanager.events.RCTModernEventEmitter; +import com.facebook.react.uimanager.events.TouchEventType; import com.facebook.systrace.Systrace; import java.util.HashSet; import java.util.Set; -public class FabricEventEmitter implements RCTEventEmitter { +public class FabricEventEmitter implements RCTModernEventEmitter { private static final String TAG = "FabricEventEmitter"; @@ -40,27 +41,62 @@ public FabricEventEmitter(@NonNull FabricUIManager uiManager) { @Override public void receiveEvent(int reactTag, @NonNull String eventName, @Nullable WritableMap params) { + receiveEvent(-1, reactTag, eventName, params); + } + + @Override + public void receiveEvent( + int surfaceId, int reactTag, String eventName, @Nullable WritableMap params) { + receiveEvent(surfaceId, reactTag, eventName, false, 0, params, EventCategoryDef.UNSPECIFIED); + } + + @Override + public void receiveEvent( + int surfaceId, + int reactTag, + String eventName, + boolean canCoalesceEvent, + int customCoalesceKey, + @Nullable WritableMap params, + @EventCategoryDef int category) { Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricEventEmitter.receiveEvent('" + eventName + "')"); - mUIManager.receiveEvent(reactTag, eventName, params); + mUIManager.receiveEvent( + surfaceId, reactTag, eventName, canCoalesceEvent, customCoalesceKey, params, category); Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } + /** + * Processes touches in a JS compatible way and send it to Fabric core + * + * @param eventName the event name (see {@link TouchEventType}) + * @param touches all the touch data extracted from MotionEvent + * @param changedIndices the indices of the pointers that changed (MOVE/CANCEL includes all + * touches, START/END only the one that was added/removed) + */ @Override public void receiveTouches( - @NonNull String eventTopLevelType, + @NonNull String eventName, @NonNull WritableArray touches, @NonNull WritableArray changedIndices) { + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "FabricEventEmitter.receiveTouches('" + eventName + "')"); + + boolean isFinalEvent = + TouchEventType.END.getJsName().equalsIgnoreCase(eventName) + || TouchEventType.CANCEL.getJsName().equalsIgnoreCase(eventName); + Pair result = - TOP_TOUCH_END_KEY.equalsIgnoreCase(eventTopLevelType) - || TOP_TOUCH_CANCEL_KEY.equalsIgnoreCase(eventTopLevelType) + isFinalEvent ? removeTouchesAtIndices(touches, changedIndices) : touchSubsequence(touches, changedIndices); WritableArray changedTouches = result.first; touches = result.second; + int eventCategory = getTouchCategory(eventName); for (int jj = 0; jj < changedTouches.size(); jj++) { WritableMap touch = getWritableMap(changedTouches.getMap(jj)); // Touch objects can fulfill the role of `DOM` `Event` objects if we set @@ -70,15 +106,18 @@ public void receiveTouches( touch.putArray(TOUCHES_KEY, copyWritableArray(touches)); WritableMap nativeEvent = touch; int rootNodeID = 0; - int target = nativeEvent.getInt(TARGET_KEY); - if (target < 1) { + int targetSurfaceId = nativeEvent.getInt(TARGET_SURFACE_KEY); + int targetReactTag = nativeEvent.getInt(TARGET_KEY); + if (targetReactTag < 1) { FLog.e(TAG, "A view is reporting that a touch occurred on tag zero."); } else { - rootNodeID = target; + rootNodeID = targetReactTag; } - receiveEvent(rootNodeID, eventTopLevelType, touch); + receiveEvent(targetSurfaceId, rootNodeID, eventName, false, 0, touch, eventCategory); } + + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } /** TODO T31905686 optimize this to avoid copying arrays */ @@ -154,4 +193,19 @@ private WritableArray copyWritableArray(@NonNull WritableArray array) { map.merge(readableMap); return map; } + + @EventCategoryDef + private static int getTouchCategory(String touchEventType) { + int category = EventCategoryDef.UNSPECIFIED; + if (TouchEventType.MOVE.getJsName().equals(touchEventType)) { + category = EventCategoryDef.CONTINUOUS; + } else if (TouchEventType.START.getJsName().equals(touchEventType)) { + category = EventCategoryDef.CONTINUOUS_START; + } else if (TouchEventType.END.getJsName().equals(touchEventType) + || TouchEventType.CANCEL.getJsName().equals(touchEventType)) { + category = EventCategoryDef.CONTINUOUS_END; + } + + return category; + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Android.mk index 593a8cf30815b..1c2bde705d6f1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Android.mk @@ -11,7 +11,7 @@ LOCAL_MODULE := fabricjni LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) -LOCAL_SHARED_LIBRARIES := libreactconfig libreact_render_components_slider libreact_render_components_progressbar libreact_render_components_switch libreact_render_components_modal libyoga libglog libfb libfbjni libglog_init libfolly_json libfolly_futures libreact_render_mounting libreactnativeutilsjni libreact_utils libreact_render_debug libreact_render_graphics libreact_render_core libreact_render_mapbuffer react_render_componentregistry libreact_render_components_view libreact_render_components_unimplementedview libreact_render_components_root libreact_render_components_scrollview libbetter libreact_render_attributedstring libreact_render_uimanager libreact_render_templateprocessor libreact_render_scheduler libreact_render_animations libreact_render_imagemanager libreact_render_textlayoutmanager libreact_render_viewmanagers react_render_components_text libreact_render_components_image react_render_components_textinput react_render_components_picker +LOCAL_SHARED_LIBRARIES := libjsi libreactconfig librrc_slider librrc_progressbar librrc_switch librrc_modal libyoga libglog libfb libfbjni libglog_init libfolly_json libfolly_futures libreact_render_mounting libreactnativeutilsjni libreact_utils libreact_render_debug libreact_render_graphics libreact_render_core react_render_componentregistry librrc_view librrc_unimplementedview librrc_root librrc_scrollview libbetter libreact_render_attributedstring libreact_render_uimanager libreact_render_templateprocessor libreact_render_scheduler libreact_render_animations libreact_render_imagemanager libreact_render_textlayoutmanager libreact_codegen_rncore rrc_text librrc_image librrc_textinput libreact_debug libreact_render_mapbuffer libmapbufferjni libreact_render_telemetry libreact_render_runtimescheduler LOCAL_STATIC_LIBRARIES := @@ -22,7 +22,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) @@ -34,6 +34,7 @@ $(call import-module,yogajni) $(call import-module,glog) $(call import-module,react/utils) +$(call import-module,react/debug) $(call import-module,react/config) $(call import-module,react/renderer/animations) $(call import-module,react/renderer/attributedstring) @@ -42,7 +43,6 @@ $(call import-module,react/renderer/core) $(call import-module,react/renderer/components/image) $(call import-module,react/renderer/components/modal) $(call import-module,react/renderer/components/root) -$(call import-module,react/renderer/components/picker) $(call import-module,react/renderer/components/progressbar) $(call import-module,react/renderer/components/scrollview) $(call import-module,react/renderer/components/slider) @@ -56,9 +56,9 @@ $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/imagemanager) $(call import-module,react/renderer/mapbuffer) $(call import-module,react/renderer/mounting) +$(call import-module,react/renderer/runtimescheduler) $(call import-module,react/renderer/scheduler) $(call import-module,react/renderer/templateprocessor) $(call import-module,react/renderer/textlayoutmanager) $(call import-module,react/renderer/uimanager) - -# $(call import-module,react/fabric/viewmanagers/jni) +$(call import-module,react/renderer/telemetry) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.cpp new file mode 100644 index 0000000000000..1d9b2dd27f2f6 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +#include "AsyncEventBeatV2.h" + +namespace facebook::react { + +AsyncEventBeatV2::AsyncEventBeatV2( + EventBeat::SharedOwnerBox const &ownerBox, + EventBeatManager *eventBeatManager, + RuntimeExecutor runtimeExecutor, + jni::global_ref javaUIManager) + : EventBeat(ownerBox), + eventBeatManager_(eventBeatManager), + runtimeExecutor_(runtimeExecutor), + javaUIManager_(javaUIManager) { + eventBeatManager->addObserver(*this); +} + +AsyncEventBeatV2::~AsyncEventBeatV2() { + eventBeatManager_->removeObserver(*this); +} + +void AsyncEventBeatV2::tick() const { + if (!isRequested_ || isBeatCallbackScheduled_) { + return; + } + + isRequested_ = false; + isBeatCallbackScheduled_ = true; + + runtimeExecutor_([this, ownerBox = ownerBox_](jsi::Runtime &runtime) { + isBeatCallbackScheduled_ = false; + auto owner = ownerBox->owner.lock(); + if (!owner) { + return; + } + + if (beatCallback_) { + beatCallback_(runtime); + } + }); +} + +void AsyncEventBeatV2::induce() const { + tick(); +} + +void AsyncEventBeatV2::request() const { + bool alreadyRequested = isRequested_; + EventBeat::request(); + if (!alreadyRequested) { + // Notifies java side that an event will be dispatched (e.g. LayoutEvent) + static auto onRequestEventBeat = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("onRequestEventBeat"); + onRequestEventBeat(javaUIManager_); + } +} + +} // namespace facebook::react diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.h b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.h new file mode 100644 index 0000000000000..acd682f4cd856 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/AsyncEventBeatV2.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "EventBeatManager.h" + +namespace facebook::react { + +class AsyncEventBeatV2 final : public EventBeat, + public EventBeatManagerObserver { + public: + AsyncEventBeatV2( + EventBeat::SharedOwnerBox const &ownerBox, + EventBeatManager *eventBeatManager, + RuntimeExecutor runtimeExecutor, + jni::global_ref javaUIManager); + + ~AsyncEventBeatV2() override; + + void tick() const override; + + void induce() const override; + + void request() const override; + + private: + EventBeatManager *eventBeatManager_; + RuntimeExecutor runtimeExecutor_; + jni::global_ref javaUIManager_; + mutable std::atomic isBeatCallbackScheduled_{false}; +}; + +} // namespace facebook::react diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK index 0d6dd21aaaaef..7ea630148b4c6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK @@ -11,15 +11,11 @@ rn_xplat_cxx_library( ], prefix = "react/fabric", ), - compiler_flags = [ - "-Wall", - "-fexceptions", - "-std=gnu++1y", - "-frtti", - ], + compiler_flags_enable_exceptions = True, + compiler_flags_enable_rtti = True, # dynamic_cast used within Binding.cpp fbandroid_allow_jni_merging = True, labels = ["supermodule:xplat/default/public.react_native.infra"], - platforms = (ANDROID), + platforms = ANDROID, preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", "-DWITH_FBSYSTRACE=1", @@ -27,7 +23,7 @@ rn_xplat_cxx_library( soname = "libfabricjni.$(ext)", visibility = ["PUBLIC"], deps = [ - react_native_xplat_target("better:better"), + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), react_native_xplat_target("react/config:config"), react_native_xplat_target("react/renderer/animations:animations"), react_native_xplat_target("react/renderer/uimanager:uimanager"), @@ -47,7 +43,6 @@ rn_xplat_cxx_library( "//xplat/js/react-native-github:generated_components-rncore", react_native_xplat_target("react/renderer/components/image:image"), react_native_xplat_target("react/renderer/components/modal:modal"), - react_native_xplat_target("react/renderer/components/picker:androidpicker"), react_native_xplat_target("react/renderer/components/slider:slider"), react_native_xplat_target("react/renderer/components/switch:androidswitch"), react_native_xplat_target("react/renderer/components/progressbar:androidprogressbar"), diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index 62571a1f7018e..79b6c1ccf3a84 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -7,11 +7,14 @@ #include "Binding.h" #include "AsyncEventBeat.h" +#include "AsyncEventBeatV2.h" #include "EventEmitterWrapper.h" #include "ReactNativeConfigHolder.h" #include "StateWrapperImpl.h" -#include +#include +#include + #include #include #include @@ -43,14 +46,6 @@ struct JMountItem : public JavaClass { "Lcom/facebook/react/fabric/mounting/mountitems/MountItem;"; }; -struct RemoveDeleteMetadata { - Tag tag; - Tag parentTag; - int index; - bool shouldRemove; - bool shouldDelete; -}; - } // namespace CppMountItem CppMountItem::CreateMountItem(ShadowView shadowView) { @@ -103,7 +98,7 @@ static inline int getIntBufferSizeForType(CppMountItem::Type mountItemType) { } else if (mountItemType == CppMountItem::Type::UpdatePadding) { return 5; // tag, top, left, bottom, right } else if (mountItemType == CppMountItem::Type::UpdateLayout) { - return 6; // tag, x, y, w, h, layoutDirection + return 6; // tag, x, y, w, h, DisplayType } else if (mountItemType == CppMountItem::Type::UpdateEventEmitter) { return 1; // tag } else { @@ -165,7 +160,8 @@ static inline void computeBufferSizes( batchMountItemIntsSize += getIntBufferSizeForType(mountItemType); if (mountItemType == CppMountItem::Type::Create) { - batchMountItemObjectsSize += 3; // component name, props, state + batchMountItemObjectsSize += + 4; // component name, props, state, event emitter } } @@ -237,6 +233,34 @@ std::shared_ptr Binding::getScheduler() { return scheduler_; } +jni::local_ref +Binding::getInspectorDataForInstance( + jni::alias_ref eventEmitterWrapper) { + std::shared_ptr scheduler = getScheduler(); + if (!scheduler) { + LOG(ERROR) << "Binding::startSurface: scheduler disappeared"; + return ReadableNativeMap::newObjectCxxArgs(folly::dynamic::object()); + } + + EventEmitterWrapper *cEventEmitter = cthis(eventEmitterWrapper); + InspectorData data = scheduler->getInspectorDataForInstance( + enableEventEmitterRawPointer_ ? *cEventEmitter->eventEmitterPointer + : *cEventEmitter->eventEmitter); + + folly::dynamic result = folly::dynamic::object; + result["fileName"] = data.fileName; + result["lineNumber"] = data.lineNumber; + result["columnNumber"] = data.columnNumber; + result["selectedIndex"] = data.selectedIndex; + result["props"] = data.props; + auto hierarchy = folly::dynamic::array(); + for (auto hierarchyItem : data.hierarchy) { + hierarchy.push_back(hierarchyItem); + } + result["hierarchy"] = hierarchy; + return ReadableNativeMap::newObjectCxxArgs(result); +} + void Binding::startSurface( jint surfaceId, jni::alias_ref moduleName, @@ -249,15 +273,27 @@ void Binding::startSurface( return; } - LayoutContext context; - context.pointScaleFactor = pointScaleFactor_; - scheduler->startSurface( - surfaceId, - moduleName->toStdString(), - initialProps->consume(), - {}, - context, + auto layoutContext = LayoutContext{}; + layoutContext.pointScaleFactor = pointScaleFactor_; + + auto surfaceHandler = SurfaceHandler{moduleName->toStdString(), surfaceId}; + surfaceHandler.setContextContainer(scheduler->getContextContainer()); + surfaceHandler.setProps(initialProps->consume()); + surfaceHandler.constraintLayout({}, layoutContext); + + scheduler->registerSurface(surfaceHandler); + + surfaceHandler.start(); + + surfaceHandler.getMountingCoordinator()->setMountingOverrideDelegate( animationDriver_); + + { + SystraceSection s2("FabricUIManagerBinding::startSurface::surfaceId::lock"); + std::unique_lock lock(surfaceHandlerRegistryMutex_); + SystraceSection s3("FabricUIManagerBinding::startSurface::surfaceId"); + surfaceHandlerRegistry_.emplace(surfaceId, std::move(surfaceHandler)); + } } void Binding::startSurfaceWithConstraints( @@ -302,13 +338,26 @@ void Binding::startSurfaceWithConstraints( constraints.layoutDirection = isRTL ? LayoutDirection::RightToLeft : LayoutDirection::LeftToRight; - scheduler->startSurface( - surfaceId, - moduleName->toStdString(), - initialProps->consume(), - constraints, - context, + auto surfaceHandler = SurfaceHandler{moduleName->toStdString(), surfaceId}; + surfaceHandler.setContextContainer(scheduler_->getContextContainer()); + surfaceHandler.setProps(initialProps->consume()); + surfaceHandler.constraintLayout(constraints, context); + + scheduler->registerSurface(surfaceHandler); + + surfaceHandler.start(); + + surfaceHandler.getMountingCoordinator()->setMountingOverrideDelegate( animationDriver_); + + { + SystraceSection s2( + "FabricUIManagerBinding::startSurfaceWithConstraints::surfaceId::lock"); + std::unique_lock lock(surfaceHandlerRegistryMutex_); + SystraceSection s3( + "FabricUIManagerBinding::startSurfaceWithConstraints::surfaceId"); + surfaceHandlerRegistry_.emplace(surfaceId, std::move(surfaceHandler)); + } } void Binding::renderTemplateToSurface(jint surfaceId, jstring uiTemplate) { @@ -340,7 +389,47 @@ void Binding::stopSurface(jint surfaceId) { return; } - scheduler->stopSurface(surfaceId); + { + std::unique_lock lock(surfaceHandlerRegistryMutex_); + + auto iterator = surfaceHandlerRegistry_.find(surfaceId); + + if (iterator == surfaceHandlerRegistry_.end()) { + LOG(ERROR) << "Binding::stopSurface: Surface with given id is not found"; + return; + } + + auto surfaceHandler = std::move(iterator->second); + surfaceHandlerRegistry_.erase(iterator); + surfaceHandler.stop(); + scheduler->unregisterSurface(surfaceHandler); + } +} + +void Binding::registerSurface(SurfaceHandlerBinding *surfaceHandlerBinding) { + auto scheduler = getScheduler(); + scheduler->registerSurface(surfaceHandlerBinding->getSurfaceHandler()); +} + +void Binding::unregisterSurface(SurfaceHandlerBinding *surfaceHandlerBinding) { + auto scheduler = getScheduler(); + scheduler->unregisterSurface(surfaceHandlerBinding->getSurfaceHandler()); +} + +static inline float scale(Float value, Float pointScaleFactor) { + std::feclearexcept(FE_ALL_EXCEPT); + float result = value * pointScaleFactor; + if (std::fetestexcept(FE_OVERFLOW)) { + LOG(ERROR) << "Binding::scale - FE_OVERFLOW - value: " << value + << " pointScaleFactor: " << pointScaleFactor + << " result: " << result; + } + if (std::fetestexcept(FE_UNDERFLOW)) { + LOG(ERROR) << "Binding::scale - FE_UNDERFLOW - value: " << value + << " pointScaleFactor: " << pointScaleFactor + << " result: " << result; + } + return result; } void Binding::setConstraints( @@ -377,14 +466,38 @@ void Binding::setConstraints( constraints.layoutDirection = isRTL ? LayoutDirection::RightToLeft : LayoutDirection::LeftToRight; - scheduler->constraintSurfaceLayout(surfaceId, constraints, context); + { + std::shared_lock lock(surfaceHandlerRegistryMutex_); + + auto iterator = surfaceHandlerRegistry_.find(surfaceId); + + if (iterator == surfaceHandlerRegistry_.end()) { + LOG(ERROR) + << "Binding::setConstraints: Surface with given id is not found"; + return; + } + + auto &surfaceHandler = iterator->second; + surfaceHandler.constraintLayout(constraints, context); + } +} + +bool isMapBufferSerializationEnabled() { + static const auto reactFeatureFlagsJavaDescriptor = + jni::findClassStatic(Binding::ReactFeatureFlagsJavaDescriptor); + static const auto isMapBufferSerializationEnabledMethod = + reactFeatureFlagsJavaDescriptor->getStaticMethod( + "isMapBufferSerializationEnabled"); + bool value = + isMapBufferSerializationEnabledMethod(reactFeatureFlagsJavaDescriptor); + return value; } void Binding::installFabricUIManager( jni::alias_ref runtimeExecutorHolder, + jni::alias_ref runtimeSchedulerHolder, jni::alias_ref javaUIManager, EventBeatManager *eventBeatManager, - jni::alias_ref jsMessageQueueThread, ComponentFactory *componentsRegistry, jni::alias_ref reactNativeConfig) { SystraceSection s("FabricUIManagerBinding::installFabricUIManager"); @@ -395,6 +508,12 @@ void Binding::installFabricUIManager( enableFabricLogs_ = config->getBool("react_fabric:enabled_android_fabric_logs"); + disableRevisionCheckForPreallocation_ = + config->getBool("react_fabric:disable_revision_check_for_preallocation"); + + enableEventEmitterRawPointer_ = + config->getBool("react_fabric:enable_event_emitter_wrapper_raw_pointer"); + if (enableFabricLogs_) { LOG(WARNING) << "Binding::installFabricUIManager() was called (address: " << this << ")."; @@ -412,47 +531,70 @@ void Binding::installFabricUIManager( ContextContainer::Shared contextContainer = std::make_shared(); - auto sharedJSMessageQueueThread = - std::make_shared(jsMessageQueueThread); auto runtimeExecutor = runtimeExecutorHolder->cthis()->get(); + if (runtimeSchedulerHolder) { + auto runtimeScheduler = runtimeSchedulerHolder->cthis()->get().lock(); + if (runtimeScheduler) { + runtimeScheduler->setEnableYielding(config->getBool( + "react_native_new_architecture:runtimescheduler_enable_yielding_android")); + runtimeExecutor = + [runtimeScheduler]( + std::function &&callback) { + runtimeScheduler->scheduleWork(std::move(callback)); + }; + } + } + + auto enableV2AsynchronousEventBeat = + config->getBool("react_fabric:enable_asynchronous_event_beat_v2_android"); + // TODO: T31905686 Create synchronous Event Beat jni::global_ref localJavaUIManager = javaUIManager_; EventBeat::Factory synchronousBeatFactory = - [eventBeatManager, runtimeExecutor, localJavaUIManager]( - EventBeat::SharedOwnerBox const &ownerBox) { - return std::make_unique( - ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); - }; + [eventBeatManager, + runtimeExecutor, + localJavaUIManager, + enableV2AsynchronousEventBeat](EventBeat::SharedOwnerBox const &ownerBox) + -> std::unique_ptr { + if (enableV2AsynchronousEventBeat) { + return std::make_unique( + ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + } else { + return std::make_unique( + ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + } + }; EventBeat::Factory asynchronousBeatFactory = - [eventBeatManager, runtimeExecutor, localJavaUIManager]( - EventBeat::SharedOwnerBox const &ownerBox) { - return std::make_unique( - ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); - }; + [eventBeatManager, + runtimeExecutor, + localJavaUIManager, + enableV2AsynchronousEventBeat](EventBeat::SharedOwnerBox const &ownerBox) + -> std::unique_ptr { + if (enableV2AsynchronousEventBeat) { + return std::make_unique( + ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + } else { + return std::make_unique( + ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); + } + }; contextContainer->insert("ReactNativeConfig", config); contextContainer->insert("FabricUIManager", javaUIManager_); // Keep reference to config object and cache some feature flags here reactNativeConfig_ = config; - collapseDeleteCreateMountingInstructions_ = - reactNativeConfig_->getBool( - "react_fabric:enabled_collapse_delete_create_mounting_instructions") && - !reactNativeConfig_->getBool( - "react_fabric:enable_reparenting_detection_android") && - !reactNativeConfig_->getBool( - "react_fabric:enabled_layout_animations_android"); - useIntBufferBatchMountItem_ = reactNativeConfig_->getBool( - "react_fabric:use_int_buffer_batch_mountitem_android"); + contextContainer->insert( + "MapBufferSerializationEnabled", isMapBufferSerializationEnabled()); disablePreallocateViews_ = reactNativeConfig_->getBool( "react_fabric:disabled_view_preallocation_android"); - bool enableLayoutAnimations = reactNativeConfig_->getBool( - "react_fabric:enabled_layout_animations_android"); + enableEarlyEventEmitterUpdate_ = reactNativeConfig_->getBool( + "react_fabric:enable_early_event_emitter_update"); auto toolbox = SchedulerToolbox{}; toolbox.contextContainer = contextContainer; @@ -467,10 +609,8 @@ void Binding::installFabricUIManager( toolbox.backgroundExecutor = backgroundExecutor_->get(); } - if (enableLayoutAnimations) { - animationDriver_ = - std::make_shared(runtimeExecutor, this); - } + animationDriver_ = std::make_shared( + runtimeExecutor, contextContainer, this); scheduler_ = std::make_shared( toolbox, (animationDriver_ ? animationDriver_.get() : nullptr), this); } @@ -506,239 +646,23 @@ inline local_ref castReadableArray( // TODO: this method will be removed when binding for components are code-gen local_ref getPlatformComponentName(const ShadowView &shadowView) { - local_ref componentName; - auto newViewProps = - std::dynamic_pointer_cast(shadowView.props); - - if (newViewProps && - newViewProps->getProbablyMoreHorizontalThanVertical_DEPRECATED()) { - componentName = make_jstring("AndroidHorizontalScrollView"); - } else { - componentName = make_jstring(shadowView.componentName); - } - return componentName; -} - -local_ref createUpdateEventEmitterMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation) { - if (!mutation.newChildShadowView.eventEmitter) { - return nullptr; - } - SharedEventEmitter eventEmitter = mutation.newChildShadowView.eventEmitter; - - // Do not hold a reference to javaEventEmitter from the C++ side. - auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); - EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); - cEventEmitter->eventEmitter = eventEmitter; - - static auto updateEventEmitterInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod(jint, jobject)>( - "updateEventEmitterMountItem"); - - return updateEventEmitterInstruction( - javaUIManager, mutation.newChildShadowView.tag, javaEventEmitter.get()); -} - -local_ref createUpdatePropsMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation) { - auto shadowView = mutation.newChildShadowView; - - // TODO: move props from map to a typed object. - auto newProps = shadowView.props->rawProps; - - local_ref readableMap = - castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps)); - static auto updatePropsInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod(jint, ReadableMap::javaobject)>( - "updatePropsMountItem"); - - return updatePropsInstruction( - javaUIManager, mutation.newChildShadowView.tag, readableMap.get()); -} - -local_ref createUpdateLayoutMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation) { - auto oldChildShadowView = mutation.oldChildShadowView; - auto newChildShadowView = mutation.newChildShadowView; - - if (newChildShadowView.layoutMetrics != EmptyLayoutMetrics && - oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) { - static auto updateLayoutInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( - jint, jint, jint, jint, jint, jint)>("updateLayoutMountItem"); - auto layoutMetrics = newChildShadowView.layoutMetrics; - auto pointScaleFactor = layoutMetrics.pointScaleFactor; - auto frame = layoutMetrics.frame; - - int x = round(frame.origin.x * pointScaleFactor); - int y = round(frame.origin.y * pointScaleFactor); - int w = round(frame.size.width * pointScaleFactor); - int h = round(frame.size.height * pointScaleFactor); - auto layoutDirection = - toInt(newChildShadowView.layoutMetrics.layoutDirection); - return updateLayoutInstruction( - javaUIManager, newChildShadowView.tag, x, y, w, h, layoutDirection); - } - - return nullptr; -} - -local_ref createUpdatePaddingMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation) { - auto oldChildShadowView = mutation.oldChildShadowView; - auto newChildShadowView = mutation.newChildShadowView; - - if (oldChildShadowView.layoutMetrics.contentInsets == - newChildShadowView.layoutMetrics.contentInsets && - mutation.type != ShadowViewMutation::Type::Insert) { - return nullptr; - } - - static auto updatePaddingInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod(jint, jint, jint, jint, jint)>( - "updatePaddingMountItem"); - - auto layoutMetrics = newChildShadowView.layoutMetrics; - auto pointScaleFactor = layoutMetrics.pointScaleFactor; - auto contentInsets = layoutMetrics.contentInsets; + static std::string scrollViewComponentName = std::string("ScrollView"); - int left = floor(contentInsets.left * pointScaleFactor); - int top = floor(contentInsets.top * pointScaleFactor); - int right = floor(contentInsets.right * pointScaleFactor); - int bottom = floor(contentInsets.bottom * pointScaleFactor); - - return updatePaddingInstruction( - javaUIManager, newChildShadowView.tag, left, top, right, bottom); -} - -local_ref createInsertMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation) { - static auto insertInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod(jint, jint, jint)>( - "insertMountItem"); - - return insertInstruction( - javaUIManager, - mutation.newChildShadowView.tag, - mutation.parentShadowView.tag, - mutation.index); -} - -local_ref createUpdateStateMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation) { - static auto updateStateInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod(jint, jobject)>( - "updateStateMountItem"); - - auto state = mutation.newChildShadowView.state; - - // Do not hold onto Java object from C - // We DO want to hold onto C object from Java, since we don't know the - // lifetime of the Java object - local_ref javaStateWrapper = nullptr; - if (state != nullptr) { - javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); - StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = state; - } - - return updateStateInstruction( - javaUIManager, - mutation.newChildShadowView.tag, - (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr)); -} - -local_ref createRemoveAndDeleteMultiMountItem( - const jni::global_ref &javaUIManager, - const std::vector &metadata) { - auto env = Environment::current(); - auto removeAndDeleteArray = env->NewIntArray(metadata.size() * 4); - int position = 0; - jint temp[4]; - for (const auto &x : metadata) { - temp[0] = x.tag; - temp[1] = x.parentTag; - temp[2] = x.index; - temp[3] = (x.shouldRemove ? 1 : 0) | (x.shouldDelete ? 2 : 0); - env->SetIntArrayRegion(removeAndDeleteArray, position, 4, temp); - position += 4; - } - - static auto removeDeleteMultiInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod(jintArray)>( - "removeDeleteMultiMountItem"); - - auto ret = removeDeleteMultiInstruction(javaUIManager, removeAndDeleteArray); - - // It is not strictly necessary to manually delete the ref here, in this - // particular case. If JNI memory is being allocated in a loop, it's easy to - // overload the localref table and crash; this is not possible in this case - // since the JNI would automatically clear this ref when it goes out of scope, - // anyway. However, this is being left here as a reminder of good hygiene and - // to be careful with JNI-allocated memory in general. - env->DeleteLocalRef(removeAndDeleteArray); - - return ret; -} - -// TODO T48019320: because we pass initial props and state to the Create (and -// preallocate) mount instruction, we technically don't need to pass the first -// Update to any components. Dedupe? -local_ref createCreateMountItem( - const jni::global_ref &javaUIManager, - const ShadowViewMutation &mutation, - const Tag surfaceId) { - static auto createJavaInstruction = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( - jstring, ReadableMap::javaobject, jobject, jint, jint, jboolean)>( - "createMountItem"); - - auto newChildShadowView = mutation.newChildShadowView; - - local_ref componentName = - getPlatformComponentName(newChildShadowView); - - jboolean isLayoutable = - newChildShadowView.layoutMetrics != EmptyLayoutMetrics; - - local_ref props = castReadableMap( - ReadableNativeMap::newObjectCxxArgs(newChildShadowView.props->rawProps)); - - // Do not hold onto Java object from C - // We DO want to hold onto C object from Java, since we don't know the - // lifetime of the Java object - local_ref javaStateWrapper = nullptr; - if (newChildShadowView.state != nullptr) { - javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); - StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = newChildShadowView.state; + local_ref componentName; + if (scrollViewComponentName.compare(shadowView.componentName) == 0) { + auto newViewProps = + std::static_pointer_cast(shadowView.props); + if (newViewProps->getProbablyMoreHorizontalThanVertical_DEPRECATED()) { + componentName = make_jstring("AndroidHorizontalScrollView"); + return componentName; + } } - return createJavaInstruction( - javaUIManager, - componentName.get(), - props.get(), - (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr), - surfaceId, - newChildShadowView.tag, - isLayoutable); + componentName = make_jstring(shadowView.componentName); + return componentName; } -void Binding::schedulerDidFinishTransactionIntBuffer( +void Binding::schedulerDidFinishTransaction( MountingCoordinator::Shared const &mountingCoordinator) { std::lock_guard lock(commitMutex_); @@ -782,8 +706,7 @@ void Binding::schedulerDidFinishTransactionIntBuffer( auto &mutationType = mutation.type; auto &index = mutation.index; - bool isVirtual = newChildShadowView.layoutMetrics == EmptyLayoutMetrics && - oldChildShadowView.layoutMetrics == EmptyLayoutMetrics; + bool isVirtual = mutation.mutatedViewIsVirtual(); switch (mutationType) { case ShadowViewMutation::Create: { @@ -949,7 +872,7 @@ void Binding::schedulerDidFinishTransactionIntBuffer( int intBufferPosition = 0; int objBufferPosition = 0; int prevMountItemType = -1; - jint temp[6]; + jint temp[7]; for (int i = 0; i < cppCommonMountItems.size(); i++) { const auto &mountItem = cppCommonMountItems[i]; const auto &mountItemType = mountItem.type; @@ -996,6 +919,16 @@ void Binding::schedulerDidFinishTransactionIntBuffer( cStateWrapper->state_ = mountItem.newChildShadowView.state; } + // Do not hold a reference to javaEventEmitter from the C++ side. + SharedEventEmitter eventEmitter = + mountItem.newChildShadowView.eventEmitter; + auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); + EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); + if (enableEventEmitterRawPointer_) { + cEventEmitter->eventEmitterPointer = eventEmitter.get(); + } else { + cEventEmitter->eventEmitter = eventEmitter; + } temp[0] = mountItem.newChildShadowView.tag; temp[1] = isLayoutable; env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp); @@ -1005,6 +938,7 @@ void Binding::schedulerDidFinishTransactionIntBuffer( (*objBufferArray)[objBufferPosition++] = props.get(); (*objBufferArray)[objBufferPosition++] = javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr; + (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get(); } else if (mountItemType == CppMountItem::Type::Insert) { temp[0] = mountItem.newChildShadowView.tag; temp[1] = mountItem.parentShadowView.tag; @@ -1081,10 +1015,10 @@ void Binding::schedulerDidFinishTransactionIntBuffer( auto pointScaleFactor = layoutMetrics.pointScaleFactor; auto contentInsets = layoutMetrics.contentInsets; - int left = floor(contentInsets.left * pointScaleFactor); - int top = floor(contentInsets.top * pointScaleFactor); - int right = floor(contentInsets.right * pointScaleFactor); - int bottom = floor(contentInsets.bottom * pointScaleFactor); + int left = floor(scale(contentInsets.left, pointScaleFactor)); + int top = floor(scale(contentInsets.top, pointScaleFactor)); + int right = floor(scale(contentInsets.right, pointScaleFactor)); + int bottom = floor(scale(contentInsets.bottom, pointScaleFactor)); temp[0] = mountItem.newChildShadowView.tag; temp[1] = left; @@ -1108,19 +1042,19 @@ void Binding::schedulerDidFinishTransactionIntBuffer( auto pointScaleFactor = layoutMetrics.pointScaleFactor; auto frame = layoutMetrics.frame; - int x = round(frame.origin.x * pointScaleFactor); - int y = round(frame.origin.y * pointScaleFactor); - int w = round(frame.size.width * pointScaleFactor); - int h = round(frame.size.height * pointScaleFactor); - int layoutDirection = - toInt(mountItem.newChildShadowView.layoutMetrics.layoutDirection); + int x = round(scale(frame.origin.x, pointScaleFactor)); + int y = round(scale(frame.origin.y, pointScaleFactor)); + int w = round(scale(frame.size.width, pointScaleFactor)); + int h = round(scale(frame.size.height, pointScaleFactor)); + int displayType = + toInt(mountItem.newChildShadowView.layoutMetrics.displayType); temp[0] = mountItem.newChildShadowView.tag; temp[1] = x; temp[2] = y; temp[3] = w; temp[4] = h; - temp[5] = layoutDirection; + temp[5] = displayType; env->SetIntArrayRegion(intBufferArray, intBufferPosition, 6, temp); intBufferPosition += 6; } @@ -1144,7 +1078,11 @@ void Binding::schedulerDidFinishTransactionIntBuffer( // Do not hold a reference to javaEventEmitter from the C++ side. auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); - cEventEmitter->eventEmitter = eventEmitter; + if (enableEventEmitterRawPointer_) { + cEventEmitter->eventEmitterPointer = eventEmitter.get(); + } else { + cEventEmitter->eventEmitter = eventEmitter; + } (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get(); } @@ -1196,295 +1134,6 @@ void Binding::schedulerDidFinishTransactionIntBuffer( env->DeleteLocalRef(intBufferArray); } -void Binding::schedulerDidFinishTransaction( - MountingCoordinator::Shared const &mountingCoordinator) { - std::lock_guard lock(commitMutex_); - - if (useIntBufferBatchMountItem_) { - return schedulerDidFinishTransactionIntBuffer(mountingCoordinator); - } - - SystraceSection s("FabricUIManagerBinding::schedulerDidFinishTransaction"); - auto finishTransactionStartTime = telemetryTimePointNow(); - - jni::global_ref localJavaUIManager = getJavaUIManager(); - if (!localJavaUIManager) { - LOG(ERROR) - << "Binding::schedulerDidFinishTransaction: JavaUIManager disappeared"; - return; - } - - auto mountingTransaction = mountingCoordinator->pullTransaction(); - - if (!mountingTransaction.has_value()) { - return; - } - - auto telemetry = mountingTransaction->getTelemetry(); - auto surfaceId = mountingTransaction->getSurfaceId(); - auto &mutations = mountingTransaction->getMutations(); - - facebook::better::set createAndDeleteTagsToProcess; - // When collapseDeleteCreateMountingInstructions_ is enabled, the - // createAndDeleteTagsToProcess set will contain all the tags belonging to - // CREATE and DELETE mutation instructions that needs to be processed. If a - // CREATE or DELETE mutation instruction does not belong in the set, it means - // that the we received a pair of mutation instructions: DELETE - CREATE and - // it is not necessary to create or delete on the screen. - if (collapseDeleteCreateMountingInstructions_) { - for (const auto &mutation : mutations) { - if (mutation.type == ShadowViewMutation::Delete) { - // TAG on 'Delete' mutation instructions are part of the - // oldChildShadowView - createAndDeleteTagsToProcess.insert(mutation.oldChildShadowView.tag); - } else if (mutation.type == ShadowViewMutation::Create) { - // TAG on 'Create' mutation instructions are part of the - // newChildShadowView - Tag tag = mutation.newChildShadowView.tag; - if (createAndDeleteTagsToProcess.find(tag) == - createAndDeleteTagsToProcess.end()) { - createAndDeleteTagsToProcess.insert(tag); - } else { - createAndDeleteTagsToProcess.erase(tag); - } - } - } - } - - auto revisionNumber = telemetry.getRevisionNumber(); - - std::vector> queue; - // Upper bound estimation of mount items to be delivered to Java side. - int size = mutations.size() * 3 + 42; - - local_ref> mountItemsArray = - JArrayClass::newArray(size); - - auto mountItems = *(mountItemsArray); - std::unordered_set deletedViewTags; - - // Find the set of tags that are removed and deleted in one block - std::vector toRemove; - - int position = 0; - for (const auto &mutation : mutations) { - auto oldChildShadowView = mutation.oldChildShadowView; - auto newChildShadowView = mutation.newChildShadowView; - auto mutationType = mutation.type; - - if (collapseDeleteCreateMountingInstructions_ && - (mutationType == ShadowViewMutation::Create || - mutationType == ShadowViewMutation::Delete) && - createAndDeleteTagsToProcess.size() > 0) { - // The TAG on 'Delete' mutation instructions are part of the - // oldChildShadowView. On the other side, the TAG on 'Create' mutation - // instructions are part of the newChildShadowView - Tag tag = mutationType == ShadowViewMutation::Create - ? mutation.newChildShadowView.tag - : mutation.oldChildShadowView.tag; - if (createAndDeleteTagsToProcess.find(tag) == - createAndDeleteTagsToProcess.end()) { - continue; - } - } - - bool isVirtual = newChildShadowView.layoutMetrics == EmptyLayoutMetrics && - oldChildShadowView.layoutMetrics == EmptyLayoutMetrics; - - // Handle accumulated removals/deletions - if (mutation.type != ShadowViewMutation::Remove && - mutation.type != ShadowViewMutation::Delete) { - if (toRemove.size() > 0) { - mountItems[position++] = - createRemoveAndDeleteMultiMountItem(localJavaUIManager, toRemove); - toRemove.clear(); - } - } - - switch (mutation.type) { - case ShadowViewMutation::Create: { - if (disablePreallocateViews_ || - mutation.newChildShadowView.props->revision > 1 || - deletedViewTags.find(mutation.newChildShadowView.tag) != - deletedViewTags.end()) { - mountItems[position++] = - createCreateMountItem(localJavaUIManager, mutation, surfaceId); - } - break; - } - case ShadowViewMutation::Remove: { - if (!isVirtual) { - toRemove.push_back( - RemoveDeleteMetadata{mutation.oldChildShadowView.tag, - mutation.parentShadowView.tag, - mutation.index, - true, - false}); - } - break; - } - case ShadowViewMutation::Delete: { - // It is impossible to delete without removing node first - const auto &it = std::find_if( - std::begin(toRemove), - std::end(toRemove), - [&mutation](const auto &x) { - return x.tag == mutation.oldChildShadowView.tag; - }); - - if (it != std::end(toRemove)) { - it->shouldDelete = true; - } else { - toRemove.push_back(RemoveDeleteMetadata{ - mutation.oldChildShadowView.tag, -1, -1, false, true}); - } - - deletedViewTags.insert(mutation.oldChildShadowView.tag); - break; - } - case ShadowViewMutation::Update: { - if (!isVirtual) { - if (mutation.oldChildShadowView.props != - mutation.newChildShadowView.props) { - mountItems[position++] = - createUpdatePropsMountItem(localJavaUIManager, mutation); - } - if (mutation.oldChildShadowView.state != - mutation.newChildShadowView.state) { - mountItems[position++] = - createUpdateStateMountItem(localJavaUIManager, mutation); - } - - // Padding: padding mountItems must be executed before layout props - // are updated in the view. This is necessary to ensure that events - // (resulting from layout changes) are dispatched with the correct - // padding information. - auto updatePaddingMountItem = - createUpdatePaddingMountItem(localJavaUIManager, mutation); - if (updatePaddingMountItem) { - mountItems[position++] = updatePaddingMountItem; - } - - auto updateLayoutMountItem = - createUpdateLayoutMountItem(localJavaUIManager, mutation); - if (updateLayoutMountItem) { - mountItems[position++] = updateLayoutMountItem; - } - } - - if (mutation.oldChildShadowView.eventEmitter != - mutation.newChildShadowView.eventEmitter) { - auto updateEventEmitterMountItem = - createUpdateEventEmitterMountItem(localJavaUIManager, mutation); - if (updateEventEmitterMountItem) { - mountItems[position++] = updateEventEmitterMountItem; - } - } - break; - } - case ShadowViewMutation::Insert: { - if (!isVirtual) { - // Insert item - mountItems[position++] = - createInsertMountItem(localJavaUIManager, mutation); - - if (disablePreallocateViews_ || - mutation.newChildShadowView.props->revision > 1 || - deletedViewTags.find(mutation.newChildShadowView.tag) != - deletedViewTags.end()) { - mountItems[position++] = - createUpdatePropsMountItem(localJavaUIManager, mutation); - } - - // State - if (mutation.newChildShadowView.state) { - mountItems[position++] = - createUpdateStateMountItem(localJavaUIManager, mutation); - } - - // Padding: padding mountItems must be executed before layout props - // are updated in the view. This is necessary to ensure that events - // (resulting from layout changes) are dispatched with the correct - // padding information. - auto updatePaddingMountItem = - createUpdatePaddingMountItem(localJavaUIManager, mutation); - if (updatePaddingMountItem) { - mountItems[position++] = updatePaddingMountItem; - } - - // Layout - auto updateLayoutMountItem = - createUpdateLayoutMountItem(localJavaUIManager, mutation); - if (updateLayoutMountItem) { - mountItems[position++] = updateLayoutMountItem; - } - } - - // EventEmitter - auto updateEventEmitterMountItem = - createUpdateEventEmitterMountItem(localJavaUIManager, mutation); - if (updateEventEmitterMountItem) { - mountItems[position++] = updateEventEmitterMountItem; - } - - break; - } - default: { - break; - } - } - } - - // Handle remaining removals and deletions - if (toRemove.size() > 0) { - mountItems[position++] = - createRemoveAndDeleteMultiMountItem(localJavaUIManager, toRemove); - toRemove.clear(); - } - - static auto createMountItemsBatchContainer = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( - jint, jtypeArray, jint, jint)>( - "createBatchMountItem"); - - // If there are no items, we pass a nullptr instead of passing the object - // through the JNI - auto batch = createMountItemsBatchContainer( - localJavaUIManager, - surfaceId, - position == 0 ? nullptr : mountItemsArray.get(), - position, - revisionNumber); - - static auto scheduleMountItem = - jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("scheduleMountItem"); - - auto finishTransactionEndTime = telemetryTimePointNow(); - - scheduleMountItem( - localJavaUIManager, - batch.get(), - telemetry.getRevisionNumber(), - telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()), - telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()), - telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()), - telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()), - telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), - telemetryTimePointToMilliseconds(finishTransactionStartTime), - telemetryTimePointToMilliseconds(finishTransactionEndTime)); -} - void Binding::setPixelDensity(float pointScaleFactor) { pointScaleFactor_ = pointScaleFactor; } @@ -1520,13 +1169,9 @@ void Binding::driveCxxAnimations() { scheduler_->animationTick(); } -void Binding::schedulerDidRequestPreliminaryViewAllocation( +void Binding::preallocateShadowView( const SurfaceId surfaceId, const ShadowView &shadowView) { - if (disablePreallocateViews_) { - return; - } - jni::global_ref localJavaUIManager = getJavaUIManager(); if (!localJavaUIManager) { LOG(ERROR) @@ -1536,15 +1181,16 @@ void Binding::schedulerDidRequestPreliminaryViewAllocation( bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics; - if (disableVirtualNodePreallocation_ && !isLayoutableShadowNode) { - return; - } - static auto preallocateView = jni::findClassStatic(Binding::UIManagerJavaDescriptor) ->getMethod( - "preallocateView"); + jint, + jint, + jstring, + ReadableMap::javaobject, + jobject, + jobject, + jboolean)>("preallocateView"); // Do not hold onto Java object from C // We DO want to hold onto C object from Java, since we don't know the @@ -1556,6 +1202,21 @@ void Binding::schedulerDidRequestPreliminaryViewAllocation( cStateWrapper->state_ = shadowView.state; } + // Do not hold a reference to javaEventEmitter from the C++ side. + local_ref javaEventEmitter = nullptr; + if (enableEarlyEventEmitterUpdate_) { + SharedEventEmitter eventEmitter = shadowView.eventEmitter; + if (eventEmitter != nullptr) { + javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs(); + EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter); + if (enableEventEmitterRawPointer_) { + cEventEmitter->eventEmitterPointer = eventEmitter.get(); + } else { + cEventEmitter->eventEmitter = eventEmitter; + } + } + } + local_ref props = castReadableMap( ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps)); auto component = getPlatformComponentName(shadowView); @@ -1567,9 +1228,55 @@ void Binding::schedulerDidRequestPreliminaryViewAllocation( component.get(), props.get(), (javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr), + (javaEventEmitter != nullptr ? javaEventEmitter.get() : nullptr), isLayoutableShadowNode); } +void Binding::schedulerDidRequestPreliminaryViewAllocation( + const SurfaceId surfaceId, + const ShadowNode &shadowNode) { + if (disablePreallocateViews_) { + return; + } + + auto shadowView = ShadowView(shadowNode); + + if (!shadowView.traits.check(ShadowNodeTraits::Trait::FormsView)) { + return; + } + + preallocateShadowView(surfaceId, shadowView); +} + +void Binding::schedulerDidCloneShadowNode( + SurfaceId surfaceId, + const ShadowNode &oldShadowNode, + const ShadowNode &newShadowNode) { + // This is only necessary if view preallocation was skipped during + // createShadowNode + + // We may need to PreAllocate a ShadowNode at this point if this is the + // earliest point it is possible to do so: + // 1. The revision is exactly 1 + // 2. At revision 0 (the old node), View Preallocation would have been skipped + + if (!disableRevisionCheckForPreallocation_) { + if (newShadowNode.getProps()->revision != 1) { + return; + } + if (oldShadowNode.getProps()->revision != 0) { + return; + } + } + + // If the new node is concrete and the old wasn't, we can preallocate + if (!oldShadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView) && + newShadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) { + auto shadowView = ShadowView(newShadowNode); + preallocateShadowView(surfaceId, shadowView); + } +} + void Binding::schedulerDidDispatchCommand( const ShadowView &shadowView, std::string const &commandName, @@ -1583,7 +1290,7 @@ void Binding::schedulerDidDispatchCommand( static auto dispatchCommand = jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod( + ->getMethod( "dispatchCommand"); local_ref command = make_jstring(commandName); @@ -1592,62 +1299,92 @@ void Binding::schedulerDidDispatchCommand( castReadableArray(ReadableNativeArray::newObjectCxxArgs(args)); dispatchCommand( - localJavaUIManager, shadowView.tag, command.get(), argsArray.get()); + localJavaUIManager, + shadowView.surfaceId, + shadowView.tag, + command.get(), + argsArray.get()); } -void Binding::schedulerDidSetJSResponder( - SurfaceId surfaceId, +void Binding::schedulerDidSendAccessibilityEvent( const ShadowView &shadowView, - const ShadowView &initialShadowView, - bool blockNativeResponder) { + std::string const &eventType) { jni::global_ref localJavaUIManager = getJavaUIManager(); if (!localJavaUIManager) { - LOG(ERROR) << "Binding::schedulerSetJSResponder: JavaUIManager disappeared"; + LOG(ERROR) + << "Binding::schedulerDidDispatchCommand: JavaUIManager disappeared"; return; } - static auto setJSResponder = + local_ref eventTypeStr = make_jstring(eventType); + + static auto sendAccessibilityEventFromJS = jni::findClassStatic(Binding::UIManagerJavaDescriptor) - ->getMethod("setJSResponder"); + ->getMethod( + "sendAccessibilityEventFromJS"); - setJSResponder( + sendAccessibilityEventFromJS( localJavaUIManager, + shadowView.surfaceId, shadowView.tag, - initialShadowView.tag, - (jboolean)blockNativeResponder); + eventTypeStr.get()); } -void Binding::schedulerDidClearJSResponder() { +void Binding::schedulerDidSetIsJSResponder( + ShadowView const &shadowView, + bool isJSResponder, + bool blockNativeResponder) { jni::global_ref localJavaUIManager = getJavaUIManager(); if (!localJavaUIManager) { - LOG(ERROR) - << "Binding::schedulerClearJSResponder: JavaUIManager disappeared"; + LOG(ERROR) << "Binding::schedulerSetJSResponder: JavaUIManager disappeared"; return; } + static auto setJSResponder = + jni::findClassStatic(Binding::UIManagerJavaDescriptor) + ->getMethod("setJSResponder"); + static auto clearJSResponder = jni::findClassStatic(Binding::UIManagerJavaDescriptor) ->getMethod("clearJSResponder"); - clearJSResponder(localJavaUIManager); + if (isJSResponder) { + setJSResponder( + localJavaUIManager, + shadowView.surfaceId, + shadowView.tag, + // The closest non-flattened ancestor of the same value if the node is + // not flattened. For now, we don't support the case when the node can + // be flattened because the only component that uses this feature - + // ScrollView - cannot be flattened. + shadowView.tag, + (jboolean)blockNativeResponder); + } else { + clearJSResponder(localJavaUIManager); + } } void Binding::registerNatives() { - registerHybrid( - {makeNativeMethod("initHybrid", Binding::initHybrid), - makeNativeMethod( - "installFabricUIManager", Binding::installFabricUIManager), - makeNativeMethod("startSurface", Binding::startSurface), - makeNativeMethod( - "startSurfaceWithConstraints", Binding::startSurfaceWithConstraints), - makeNativeMethod( - "renderTemplateToSurface", Binding::renderTemplateToSurface), - makeNativeMethod("stopSurface", Binding::stopSurface), - makeNativeMethod("setConstraints", Binding::setConstraints), - makeNativeMethod("setPixelDensity", Binding::setPixelDensity), - makeNativeMethod("driveCxxAnimations", Binding::driveCxxAnimations), - makeNativeMethod( - "uninstallFabricUIManager", Binding::uninstallFabricUIManager)}); + registerHybrid({ + makeNativeMethod("initHybrid", Binding::initHybrid), + makeNativeMethod( + "installFabricUIManager", Binding::installFabricUIManager), + makeNativeMethod("startSurface", Binding::startSurface), + makeNativeMethod( + "getInspectorDataForInstance", Binding::getInspectorDataForInstance), + makeNativeMethod( + "startSurfaceWithConstraints", Binding::startSurfaceWithConstraints), + makeNativeMethod( + "renderTemplateToSurface", Binding::renderTemplateToSurface), + makeNativeMethod("stopSurface", Binding::stopSurface), + makeNativeMethod("setConstraints", Binding::setConstraints), + makeNativeMethod("setPixelDensity", Binding::setPixelDensity), + makeNativeMethod("driveCxxAnimations", Binding::driveCxxAnimations), + makeNativeMethod( + "uninstallFabricUIManager", Binding::uninstallFabricUIManager), + makeNativeMethod("registerSurface", Binding::registerSurface), + makeNativeMethod("unregisterSurface", Binding::unregisterSurface), + }); } } // namespace react diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h index d0f13cc6e9ba2..5f261f345347c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h @@ -8,18 +8,21 @@ #pragma once #include -#include #include +#include #include #include #include #include #include + #include #include #include "ComponentFactory.h" #include "EventBeatManager.h" +#include "EventEmitterWrapper.h" #include "JBackgroundExecutor.h" +#include "SurfaceHandlerBinding.h" namespace facebook { namespace react { @@ -75,6 +78,9 @@ class Binding : public jni::HybridClass, constexpr static auto UIManagerJavaDescriptor = "com/facebook/react/fabric/FabricUIManager"; + constexpr static auto ReactFeatureFlagsJavaDescriptor = + "com/facebook/react/config/ReactFeatureFlags"; + static void registerNatives(); private: @@ -92,13 +98,16 @@ class Binding : public jni::HybridClass, jboolean isRTL, jboolean doLeftAndRightSwapInRTL); + jni::local_ref getInspectorDataForInstance( + jni::alias_ref eventEmitterWrapper); + static jni::local_ref initHybrid(jni::alias_ref); void installFabricUIManager( jni::alias_ref runtimeExecutorHolder, + jni::alias_ref runtimeSchedulerHolder, jni::alias_ref javaUIManager, EventBeatManager *eventBeatManager, - jni::alias_ref jsMessageQueueThread, ComponentFactory *componentsRegistry, jni::alias_ref reactNativeConfig); @@ -124,25 +133,39 @@ class Binding : public jni::HybridClass, void stopSurface(jint surfaceId); + void registerSurface(SurfaceHandlerBinding *surfaceHandler); + + void unregisterSurface(SurfaceHandlerBinding *surfaceHandler); + void schedulerDidFinishTransaction( MountingCoordinator::Shared const &mountingCoordinator) override; + void preallocateShadowView( + const SurfaceId surfaceId, + const ShadowView &shadowView); + void schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, - const ShadowView &shadowView) override; + const ShadowNode &shadowNode) override; + + void schedulerDidCloneShadowNode( + SurfaceId surfaceId, + const ShadowNode &oldShadowNode, + const ShadowNode &newShadowNode) override; void schedulerDidDispatchCommand( const ShadowView &shadowView, std::string const &commandName, folly::dynamic const args) override; - void schedulerDidSetJSResponder( - SurfaceId surfaceId, + void schedulerDidSendAccessibilityEvent( const ShadowView &shadowView, - const ShadowView &initialShadowView, - bool blockNativeResponder) override; + std::string const &eventType) override; - void schedulerDidClearJSResponder() override; + void schedulerDidSetIsJSResponder( + ShadowView const &shadowView, + bool isJSResponder, + bool blockNativeResponder) override; void setPixelDensity(float pointScaleFactor); @@ -164,20 +187,20 @@ class Binding : public jni::HybridClass, std::shared_ptr scheduler_; std::mutex schedulerMutex_; + better::map surfaceHandlerRegistry_{}; + better::shared_mutex + surfaceHandlerRegistryMutex_; // Protects `surfaceHandlerRegistry_`. + std::recursive_mutex commitMutex_; float pointScaleFactor_ = 1; std::shared_ptr reactNativeConfig_{nullptr}; - bool useIntBufferBatchMountItem_{false}; - bool collapseDeleteCreateMountingInstructions_{false}; bool disablePreallocateViews_{false}; - bool disableVirtualNodePreallocation_{false}; bool enableFabricLogs_{false}; - - private: - void schedulerDidFinishTransactionIntBuffer( - MountingCoordinator::Shared const &mountingCoordinator); + bool enableEarlyEventEmitterUpdate_{false}; + bool disableRevisionCheckForPreallocation_{false}; + bool enableEventEmitterRawPointer_{false}; }; } // namespace react diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/CoreComponentsRegistry.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/CoreComponentsRegistry.cpp index 560b8eaaa70fb..f92c84d625c8a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/CoreComponentsRegistry.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/CoreComponentsRegistry.cpp @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include #include @@ -64,14 +62,13 @@ CoreComponentsRegistry::sharedProviderRegistry() { concreteComponentDescriptorProvider()); providerRegistry->add( concreteComponentDescriptorProvider()); + providerRegistry->add( + concreteComponentDescriptorProvider< + AndroidHorizontalScrollContentViewComponentDescriptor>()); providerRegistry->add( concreteComponentDescriptorProvider()); providerRegistry->add(concreteComponentDescriptorProvider< AndroidDrawerLayoutComponentDescriptor>()); - providerRegistry->add(concreteComponentDescriptorProvider< - AndroidDialogPickerComponentDescriptor>()); - providerRegistry->add(concreteComponentDescriptorProvider< - AndroidDropdownPickerComponentDescriptor>()); return providerRegistry; }(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.cpp index 19fe921a46b80..b535c2ca7500a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.cpp @@ -20,15 +20,53 @@ EventEmitterWrapper::initHybrid(jni::alias_ref) { void EventEmitterWrapper::invokeEvent( std::string eventName, - NativeMap *payload) { - eventEmitter->dispatchEvent( - eventName, payload->consume(), EventPriority::AsynchronousBatched); + NativeMap *payload, + int category) { + if (eventEmitterPointer) { + eventEmitterPointer->dispatchEvent( + std::move(eventName), + payload->consume(), + EventPriority::AsynchronousBatched, + static_cast(category)); + return; + } + + // It is marginal, but possible for this to be constructed without a valid + // EventEmitter. In those cases, make sure we noop/blackhole events instead of + // crashing. + if (eventEmitter != nullptr) { + eventEmitter->dispatchEvent( + eventName, + payload->consume(), + EventPriority::AsynchronousBatched, + static_cast(category)); + } +} + +void EventEmitterWrapper::invokeUniqueEvent( + std::string eventName, + NativeMap *payload, + int customCoalesceKey) { + if (eventEmitterPointer) { + eventEmitterPointer->dispatchUniqueEvent( + std::move(eventName), payload->consume()); + return; + } + // TODO: customCoalesceKey currently unused + // It is marginal, but possible for this to be constructed without a valid + // EventEmitter. In those cases, make sure we noop/blackhole events instead of + // crashing. + if (eventEmitter != nullptr) { + eventEmitter->dispatchUniqueEvent(eventName, payload->consume()); + } } void EventEmitterWrapper::registerNatives() { registerHybrid({ makeNativeMethod("initHybrid", EventEmitterWrapper::initHybrid), makeNativeMethod("invokeEvent", EventEmitterWrapper::invokeEvent), + makeNativeMethod( + "invokeUniqueEvent", EventEmitterWrapper::invokeUniqueEvent), }); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.h b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.h index dcc84ea6320e1..b36911da928ca 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.h +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.h @@ -24,8 +24,13 @@ class EventEmitterWrapper : public jni::HybridClass { static void registerNatives(); SharedEventEmitter eventEmitter; + EventEmitter const *eventEmitterPointer; - void invokeEvent(std::string eventName, NativeMap *params); + void invokeEvent(std::string eventName, NativeMap *params, int category); + void invokeUniqueEvent( + std::string eventName, + NativeMap *params, + int customCoalesceKey); private: static jni::local_ref initHybrid(jni::alias_ref); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp index 03005372a658a..61fcec3d1c988 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/OnLoad.cpp @@ -13,6 +13,7 @@ #include "EventBeatManager.h" #include "EventEmitterWrapper.h" #include "StateWrapperImpl.h" +#include "SurfaceHandlerBinding.h" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { return facebook::jni::initialize(vm, [] { @@ -22,5 +23,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { facebook::react::StateWrapperImpl::registerNatives(); facebook::react::ComponentFactory::registerNatives(); facebook::react::CoreComponentsRegistry::registerNatives(); + facebook::react::SurfaceHandlerBinding::registerNatives(); }); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.cpp index afbe9c7155232..eaa9398de60b0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.cpp @@ -8,6 +8,8 @@ #include "StateWrapperImpl.h" #include #include +#include +#include using namespace facebook::jni; @@ -22,52 +24,37 @@ jni::local_ref StateWrapperImpl::initHybrid( return makeCxxInstance(); } -jni::local_ref StateWrapperImpl::getState() { +jni::local_ref +StateWrapperImpl::getStateDataImpl() { folly::dynamic map = state_->getDynamic(); local_ref readableNativeMap = ReadableNativeMap::newObjectCxxArgs(map); return readableNativeMap; } -void StateWrapperImpl::updateStateImpl(NativeMap *map) { - // Get folly::dynamic from map - auto dynamicMap = map->consume(); - // Set state - state_->updateState(dynamicMap, nullptr); +jni::local_ref +StateWrapperImpl::getStateMapBufferDataImpl() { + MapBuffer map = state_->getMapBuffer(); + auto ReadableMapBuffer = + ReadableMapBuffer::createWithContents(std::move(map)); + return ReadableMapBuffer; } -void StateWrapperImpl::updateStateWithFailureCallbackImpl( - NativeMap *map, - jni::alias_ref self, - int callbackRefId) { +void StateWrapperImpl::updateStateImpl(NativeMap *map) { // Get folly::dynamic from map auto dynamicMap = map->consume(); - // Turn the alias into a global_ref - // Note: this whole thing feels really janky, making StateWrapperImpl.java - // pass "this" into a function it's calling on "this". But after struggling - // for a while I couldn't figure out how to get a reference to the Java side - // of "this" in C++ in a way that's reasonably safe, and it maybe is even - // discouraged. Anyway, it might be weird, but this seems to work and be safe. - jni::global_ref globalSelf = make_global(self); // Set state - state_->updateState( - dynamicMap, [globalSelf = std::move(globalSelf), callbackRefId]() { - static auto method = - jni::findClassStatic( - StateWrapperImpl::StateWrapperImplJavaDescriptor) - ->getMethod("updateStateFailed"); - method(globalSelf, callbackRefId); - }); + state_->updateState(dynamicMap); } void StateWrapperImpl::registerNatives() { registerHybrid({ makeNativeMethod("initHybrid", StateWrapperImpl::initHybrid), - makeNativeMethod("getState", StateWrapperImpl::getState), + makeNativeMethod("getStateDataImpl", StateWrapperImpl::getStateDataImpl), makeNativeMethod("updateStateImpl", StateWrapperImpl::updateStateImpl), makeNativeMethod( - "updateStateWithFailureCallbackImpl", - StateWrapperImpl::updateStateWithFailureCallbackImpl), + "getStateMapBufferDataImpl", + StateWrapperImpl::getStateMapBufferDataImpl), }); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.h b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.h index 9a88eb0271bc9..9193efc2518d4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.h +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/StateWrapperImpl.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -25,7 +26,8 @@ class StateWrapperImpl : public jni::HybridClass { static void registerNatives(); - jni::local_ref getState(); + jni::local_ref getStateMapBufferDataImpl(); + jni::local_ref getStateDataImpl(); void updateStateImpl(NativeMap *map); void updateStateWithFailureCallbackImpl( NativeMap *map, diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp new file mode 100644 index 0000000000000..0df81d2a8992e --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "SurfaceHandlerBinding.h" +#include + +namespace facebook { +namespace react { + +SurfaceHandlerBinding::SurfaceHandlerBinding( + SurfaceId surfaceId, + std::string const &moduleName) + : surfaceHandler_(moduleName, surfaceId) {} + +void SurfaceHandlerBinding::setDisplayMode(jint mode) { + surfaceHandler_.setDisplayMode(static_cast(mode)); +} + +void SurfaceHandlerBinding::start() { + std::unique_lock lock(lifecycleMutex_); + + if (surfaceHandler_.getStatus() != SurfaceHandler::Status::Running) { + surfaceHandler_.start(); + } +} + +void SurfaceHandlerBinding::stop() { + std::unique_lock lock(lifecycleMutex_); + + if (surfaceHandler_.getStatus() == SurfaceHandler::Status::Running) { + surfaceHandler_.stop(); + } +} + +jint SurfaceHandlerBinding::getSurfaceId() { + return surfaceHandler_.getSurfaceId(); +} + +void SurfaceHandlerBinding::setSurfaceId(jint surfaceId) { + surfaceHandler_.setSurfaceId(surfaceId); +} + +jboolean SurfaceHandlerBinding::isRunning() { + return surfaceHandler_.getStatus() == SurfaceHandler::Status::Running; +} + +jni::local_ref SurfaceHandlerBinding::getModuleName() { + return jni::make_jstring(surfaceHandler_.getModuleName()); +} + +jni::local_ref +SurfaceHandlerBinding::initHybrid( + jni::alias_ref, + jint surfaceId, + jni::alias_ref moduleName) { + auto env = jni::Environment::current(); + const char *moduleNameValue = + env->GetStringUTFChars(moduleName.get(), JNI_FALSE); + env->ReleaseStringUTFChars(moduleName.get(), moduleNameValue); + + return makeCxxInstance(surfaceId, moduleNameValue); +} + +void SurfaceHandlerBinding::setLayoutConstraints( + jfloat minWidth, + jfloat maxWidth, + jfloat minHeight, + jfloat maxHeight, + jfloat offsetX, + jfloat offsetY, + jboolean doLeftAndRightSwapInRTL, + jboolean isRTL, + jfloat pixelDensity) { + LayoutConstraints constraints = {}; + constraints.minimumSize = {minWidth, minHeight}; + constraints.maximumSize = {maxWidth, maxHeight}; + constraints.layoutDirection = + isRTL ? LayoutDirection::RightToLeft : LayoutDirection::LeftToRight; + + LayoutContext context = {}; + context.swapLeftAndRightInRTL = doLeftAndRightSwapInRTL; + context.pointScaleFactor = pixelDensity; + context.viewportOffset = {offsetX, offsetY}; + + surfaceHandler_.constraintLayout(constraints, context); +} + +void SurfaceHandlerBinding::setProps(NativeMap *props) { + surfaceHandler_.setProps(props->consume()); +} + +SurfaceHandler const &SurfaceHandlerBinding::getSurfaceHandler() { + return surfaceHandler_; +} + +void SurfaceHandlerBinding::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", SurfaceHandlerBinding::initHybrid), + makeNativeMethod( + "getSurfaceIdNative", SurfaceHandlerBinding::getSurfaceId), + makeNativeMethod( + "setSurfaceIdNative", SurfaceHandlerBinding::setSurfaceId), + makeNativeMethod("isRunningNative", SurfaceHandlerBinding::isRunning), + makeNativeMethod( + "getModuleNameNative", SurfaceHandlerBinding::getModuleName), + makeNativeMethod("startNative", SurfaceHandlerBinding::start), + makeNativeMethod("stopNative", SurfaceHandlerBinding::stop), + makeNativeMethod( + "setLayoutConstraintsNative", + SurfaceHandlerBinding::setLayoutConstraints), + makeNativeMethod("setPropsNative", SurfaceHandlerBinding::setProps), + makeNativeMethod( + "setDisplayModeNative", SurfaceHandlerBinding::setDisplayMode), + }); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h new file mode 100644 index 0000000000000..27158eb2880bb --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/SurfaceHandlerBinding.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +class SurfaceHandlerBinding : public jni::HybridClass { + public: + constexpr static const char *const kJavaDescriptor = + "Lcom/facebook/react/fabric/SurfaceHandlerBinding;"; + + static void registerNatives(); + + SurfaceHandlerBinding(SurfaceId surfaceId, std::string const &moduleName); + + void start(); + void stop(); + + void setDisplayMode(jint mode); + + void registerScheduler(std::shared_ptr scheduler); + void unregisterScheduler(std::shared_ptr scheduler); + + jint getSurfaceId(); + void setSurfaceId(jint surfaceId); + jni::local_ref getModuleName(); + + jboolean isRunning(); + + void setLayoutConstraints( + jfloat minWidth, + jfloat maxWidth, + jfloat minHeight, + jfloat maxHeight, + jfloat offsetX, + jfloat offsetY, + jboolean doLeftAndRightSwapInRTL, + jboolean isRTL, + jfloat pixelDensity); + + void setProps(NativeMap *props); + + SurfaceHandler const &getSurfaceHandler(); + + private: + mutable better::shared_mutex lifecycleMutex_; + const SurfaceHandler surfaceHandler_; + + jni::alias_ref jhybridobject_; + + static jni::local_ref initHybrid( + jni::alias_ref, + jint surfaceId, + jni::alias_ref moduleName); +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java new file mode 100644 index 0000000000000..ca2c7df44f148 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.fabric.mounting; + +import static com.facebook.infer.annotation.ThreadConfined.ANY; +import static com.facebook.infer.annotation.ThreadConfined.UI; +import static com.facebook.react.fabric.FabricUIManager.ENABLE_FABRIC_LOGS; +import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT; + +import android.os.SystemClock; +import android.view.View; +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import com.facebook.common.logging.FLog; +import com.facebook.infer.annotation.ThreadConfined; +import com.facebook.react.bridge.ReactIgnorableMountingException; +import com.facebook.react.bridge.ReactNoCrashSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; +import com.facebook.react.bridge.RetryableMountingLayerException; +import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem; +import com.facebook.react.fabric.mounting.mountitems.MountItem; +import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem; +import com.facebook.systrace.Systrace; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class MountItemDispatcher { + + private static final String TAG = "MountItemDispatcher"; + private static final int FRAME_TIME_MS = 16; + private static final int MAX_TIME_IN_FRAME_FOR_NON_BATCHED_OPERATIONS_MS = 8; + + private final MountingManager mMountingManager; + private final ItemDispatchListener mItemDispatchListener; + + @NonNull + private final ConcurrentLinkedQueue mViewCommandMountItems = + new ConcurrentLinkedQueue<>(); + + @NonNull + private final ConcurrentLinkedQueue mMountItems = new ConcurrentLinkedQueue<>(); + + @NonNull + private final ConcurrentLinkedQueue mPreMountItems = + new ConcurrentLinkedQueue<>(); + + private boolean mInDispatch = false; + private int mReDispatchCounter = 0; + private long mBatchedExecutionTime = 0L; + private long mRunStartTime = 0L; + + public MountItemDispatcher(MountingManager mountingManager, ItemDispatchListener listener) { + mMountingManager = mountingManager; + mItemDispatchListener = listener; + } + + @AnyThread + @ThreadConfined(ANY) + public void dispatchCommandMountItem(DispatchCommandMountItem command) { + addViewCommandMountItem(command); + } + + public void addMountItem(MountItem mountItem) { + mMountItems.add(mountItem); + } + + public void addPreAllocateMountItem(PreAllocateViewMountItem mountItem) { + // We do this check only for PreAllocateViewMountItem - and not DispatchMountItem or regular + // MountItem - because PreAllocateViewMountItem is not batched, and is relatively more expensive + // both to queue, to drain, and to execute. + if (!mMountingManager.surfaceIsStopped(mountItem.getSurfaceId())) { + mPreMountItems.add(mountItem); + } else if (IS_DEVELOPMENT_ENVIRONMENT) { + FLog.e( + TAG, + "Not queueing PreAllocateMountItem: surfaceId stopped: [%d] - %s", + mountItem.getSurfaceId(), + mountItem.toString()); + } + } + + public void addViewCommandMountItem(DispatchCommandMountItem mountItem) { + mViewCommandMountItems.add(mountItem); + } + + /** + * Try to dispatch MountItems. Returns true if any items were dispatched, false otherwise. A + * `false` return value doesn't indicate errors, it may just indicate there was no work to be + * done. + * + * @return + */ + @UiThread + @ThreadConfined(UI) + public boolean tryDispatchMountItems() { + // If we're already dispatching, don't reenter. + // Reentrance can potentially happen a lot on Android in Fabric because + // `updateState` from the + // mounting layer causes mount items to be dispatched synchronously. We want to 1) make sure + // we don't reenter in those cases, but 2) still execute those queued instructions + // synchronously. + // This is a pretty blunt tool, but we might not have better options since we really don't want + // to execute anything out-of-order. + if (mInDispatch) { + return false; + } + + final boolean didDispatchItems; + try { + didDispatchItems = dispatchMountItems(); + } catch (Throwable e) { + mReDispatchCounter = 0; + throw e; + } finally { + // Clean up after running dispatchMountItems - even if an exception was thrown + mInDispatch = false; + } + + mItemDispatchListener.didDispatchMountItems(); + + // Decide if we want to try reentering + if (mReDispatchCounter < 10 && didDispatchItems) { + // Executing twice in a row is normal. Only log after that point. + if (mReDispatchCounter > 2) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Re-dispatched " + + mReDispatchCounter + + " times. This indicates setState (?) is likely being called too many times during mounting.")); + } + + mReDispatchCounter++; + tryDispatchMountItems(); + } + mReDispatchCounter = 0; + return didDispatchItems; + } + + @UiThread + @ThreadConfined(UI) + public void dispatchMountItems(Queue mountItems) { + while (!mountItems.isEmpty()) { + MountItem item = mountItems.poll(); + try { + item.execute(mMountingManager); + } catch (RetryableMountingLayerException e) { + if (item instanceof DispatchCommandMountItem) { + // Only DispatchCommandMountItem supports retries + DispatchCommandMountItem mountItem = (DispatchCommandMountItem) item; + // Retrying exactly once + if (mountItem.getRetries() == 0) { + mountItem.incrementRetries(); + // In case we haven't retried executing this item yet, execute in the next batch of + // items + dispatchCommandMountItem(mountItem); + } + } else { + printMountItem( + item, "dispatchExternalMountItems: mounting failed with " + e.getMessage()); + } + } + } + } + + @UiThread + @ThreadConfined(UI) + /** Nothing should call this directly except for `tryDispatchMountItems`. */ + private boolean dispatchMountItems() { + if (mReDispatchCounter == 0) { + mBatchedExecutionTime = 0; + } + + mRunStartTime = SystemClock.uptimeMillis(); + + List viewCommandMountItemsToDispatch = + getAndResetViewCommandMountItems(); + List mountItemsToDispatch = getAndResetMountItems(); + + if (mountItemsToDispatch == null && viewCommandMountItemsToDispatch == null) { + return false; + } + + // As an optimization, execute all ViewCommands first + // This should be: + // 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always + // wanted to be as "synchronous" as possible. + // 2) Safer: ViewCommands are inherently disconnected from the tree commit/diff/mount process. + // JS imperatively queues these commands. + // If JS has queued a command, it's reasonable to assume that the more time passes, the more + // likely it is that the view disappears. + // Thus, by executing ViewCommands early, we should actually avoid a category of + // errors/glitches. + if (viewCommandMountItemsToDispatch != null) { + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "FabricUIManager::mountViews viewCommandMountItems to execute: " + + viewCommandMountItemsToDispatch.size()); + for (DispatchCommandMountItem command : viewCommandMountItemsToDispatch) { + if (ENABLE_FABRIC_LOGS) { + printMountItem(command, "dispatchMountItems: Executing viewCommandMountItem"); + } + try { + executeOrEnqueue(command); + } catch (RetryableMountingLayerException e) { + // If the exception is marked as Retryable, we retry the viewcommand exactly once, after + // the current batch of mount items has finished executing. + if (command.getRetries() == 0) { + command.incrementRetries(); + dispatchCommandMountItem(command); + } else { + // It's very common for commands to be executed on views that no longer exist - for + // example, a blur event on TextInput being fired because of a navigation event away + // from the current screen. If the exception is marked as Retryable, we log a soft + // exception but never crash in debug. + // It's not clear that logging this is even useful, because these events are very + // common, mundane, and there's not much we can do about them currently. + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Caught exception executing ViewCommand: " + command.toString(), e)); + } + } catch (Throwable e) { + // Non-Retryable exceptions are logged as soft exceptions in prod, but crash in Debug. + ReactSoftExceptionLogger.logSoftException( + TAG, + new RuntimeException( + "Caught exception executing ViewCommand: " + command.toString(), e)); + } + } + + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } + + // If there are MountItems to dispatch, we make sure all the "pre mount items" are executed + // first + Collection preMountItemsToDispatch = getAndResetPreMountItems(); + + if (preMountItemsToDispatch != null) { + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "FabricUIManager::mountViews preMountItems to execute: " + + preMountItemsToDispatch.size()); + + for (PreAllocateViewMountItem preMountItem : preMountItemsToDispatch) { + executeOrEnqueue(preMountItem); + } + + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } + + if (mountItemsToDispatch != null) { + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "FabricUIManager::mountViews mountItems to execute: " + mountItemsToDispatch.size()); + + long batchedExecutionStartTime = SystemClock.uptimeMillis(); + + for (MountItem mountItem : mountItemsToDispatch) { + if (ENABLE_FABRIC_LOGS) { + printMountItem(mountItem, "dispatchMountItems: Executing mountItem"); + } + + try { + executeOrEnqueue(mountItem); + } catch (Throwable e) { + // If there's an exception, we want to log diagnostics in prod and rethrow. + FLog.e(TAG, "dispatchMountItems: caught exception, displaying mount state", e); + for (MountItem m : mountItemsToDispatch) { + printMountItem(m, "dispatchMountItems: mountItem"); + } + if (mountItem.getSurfaceId() != View.NO_ID) { + SurfaceMountingManager surfaceManager = + mMountingManager.getSurfaceManager(mountItem.getSurfaceId()); + if (surfaceManager != null) { + surfaceManager.printSurfaceState(); + } + } + + if (ReactIgnorableMountingException.isIgnorable(e)) { + ReactSoftExceptionLogger.logSoftException(TAG, e); + } else { + throw e; + } + } + } + mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime; + } + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + + return true; + } + + @UiThread + @ThreadConfined(UI) + public void dispatchPreMountItems(long frameTimeNanos) { + Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager::premountViews"); + + // dispatchPreMountItems cannot be reentrant, but we want to prevent dispatchMountItems from + // reentering during dispatchPreMountItems + mInDispatch = true; + + try { + while (true) { + if (haveExceededNonBatchedFrameTime(frameTimeNanos)) { + break; + } + + PreAllocateViewMountItem preMountItemToDispatch = mPreMountItems.poll(); + // If list is empty, `poll` will return null, or var will never be set + if (preMountItemToDispatch == null) { + break; + } + + if (ENABLE_FABRIC_LOGS) { + printMountItem( + preMountItemToDispatch, + "dispatchPreMountItems: Dispatching PreAllocateViewMountItem"); + } + + executeOrEnqueue(preMountItemToDispatch); + } + } finally { + mInDispatch = false; + } + + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } + + private void executeOrEnqueue(MountItem item) { + if (mMountingManager.isWaitingForViewAttach(item.getSurfaceId())) { + if (ENABLE_FABRIC_LOGS) { + FLog.e( + TAG, + "executeOrEnqueue: Item execution delayed, surface %s is not ready yet", + item.getSurfaceId()); + } + SurfaceMountingManager surfaceMountingManager = + mMountingManager.getSurfaceManager(item.getSurfaceId()); + surfaceMountingManager.executeOnViewAttach(item); + } else { + item.execute(mMountingManager); + } + } + + @Nullable + private static List drainConcurrentItemQueue( + ConcurrentLinkedQueue queue) { + List result = new ArrayList<>(); + while (!queue.isEmpty()) { + E item = queue.poll(); + if (item != null) { + result.add(item); + } + } + if (result.size() == 0) { + return null; + } + return result; + } + + /** Detect if we still have processing time left in this frame. */ + private static boolean haveExceededNonBatchedFrameTime(long frameTimeNanos) { + long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000); + return timeLeftInFrame < MAX_TIME_IN_FRAME_FOR_NON_BATCHED_OPERATIONS_MS; + } + + @UiThread + @ThreadConfined(UI) + private List getAndResetViewCommandMountItems() { + return drainConcurrentItemQueue(mViewCommandMountItems); + } + + @UiThread + @ThreadConfined(UI) + private List getAndResetMountItems() { + return drainConcurrentItemQueue(mMountItems); + } + + private Collection getAndResetPreMountItems() { + return drainConcurrentItemQueue(mPreMountItems); + } + + public long getBatchedExecutionTime() { + return mBatchedExecutionTime; + } + + public long getRunStartTime() { + return mRunStartTime; + } + + private static void printMountItem(MountItem mountItem, String prefix) { + // If a MountItem description is split across multiple lines, it's because it's a + // compound MountItem. Log each line separately. + String[] mountItemLines = mountItem.toString().split("\n"); + for (String m : mountItemLines) { + FLog.e(TAG, prefix + ": " + m); + } + } + + public interface ItemDispatchListener { + void didDispatchMountItems(); + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 74869a0ee4519..7e00a20defd00 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -10,736 +10,352 @@ import static com.facebook.infer.annotation.ThreadConfined.ANY; import static com.facebook.infer.annotation.ThreadConfined.UI; -import android.content.Context; +import android.text.Spannable; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.facebook.common.logging.FLog; -import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.ThreadConfined; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.RetryableMountingLayerException; -import com.facebook.react.bridge.SoftAssertions; import com.facebook.react.bridge.UiThreadUtil; -import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.fabric.FabricUIManager; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.mounting.mountitems.MountItem; import com.facebook.react.touch.JSResponderHandler; -import com.facebook.react.uimanager.IllegalViewOperationException; -import com.facebook.react.uimanager.ReactStylesDiffMap; -import com.facebook.react.uimanager.RootView; import com.facebook.react.uimanager.RootViewManager; -import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewManagerRegistry; +import com.facebook.react.views.text.ReactTextViewManagerCallback; +import com.facebook.react.views.text.TextLayoutManagerMapBuffer; import com.facebook.yoga.YogaMeasureMode; +import java.util.Map; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; /** * Class responsible for actually dispatching view updates enqueued via {@link - * FabricUIManager#scheduleMountItems(int, MountItem[])} on the UI thread. + * FabricUIManager#scheduleMountItem} on the UI thread. */ public class MountingManager { public static final String TAG = MountingManager.class.getSimpleName(); - private static final boolean SHOW_CHANGED_VIEW_HIERARCHIES = ReactBuildConfig.DEBUG && false; + private static final int MAX_STOPPED_SURFACE_IDS_LENGTH = 15; + + @NonNull + private final ConcurrentHashMap mSurfaceIdToManager = + new ConcurrentHashMap<>(); // any thread + + private final CopyOnWriteArrayList mStoppedSurfaceIds = new CopyOnWriteArrayList<>(); + + @Nullable private SurfaceMountingManager mMostRecentSurfaceMountingManager; + @Nullable private SurfaceMountingManager mLastQueriedSurfaceMountingManager; - @NonNull private final ConcurrentHashMap mTagToViewState; @NonNull private final JSResponderHandler mJSResponderHandler = new JSResponderHandler(); @NonNull private final ViewManagerRegistry mViewManagerRegistry; + @NonNull private final MountItemExecutor mMountItemExecutor; @NonNull private final RootViewManager mRootViewManager = new RootViewManager(); - public MountingManager(@NonNull ViewManagerRegistry viewManagerRegistry) { - mTagToViewState = new ConcurrentHashMap<>(); + public interface MountItemExecutor { + @UiThread + @ThreadConfined(UI) + void executeItems(Queue items); + } + + public MountingManager( + @NonNull ViewManagerRegistry viewManagerRegistry, + @NonNull MountItemExecutor mountItemExecutor) { mViewManagerRegistry = viewManagerRegistry; + mMountItemExecutor = mountItemExecutor; } - private static void logViewHierarchy(ViewGroup parent, boolean recurse) { - int parentTag = parent.getId(); - FLog.e(TAG, " "); - for (int i = 0; i < parent.getChildCount(); i++) { - FLog.e( - TAG, - " "); - } - FLog.e(TAG, " "); - - if (recurse) { - FLog.e(TAG, "Displaying Ancestors:"); - ViewParent ancestor = parent.getParent(); - while (ancestor != null) { - ViewGroup ancestorViewGroup = (ancestor instanceof ViewGroup ? (ViewGroup) ancestor : null); - int ancestorId = ancestorViewGroup == null ? View.NO_ID : ancestorViewGroup.getId(); - FLog.e( - TAG, - ""); - ancestor = ancestor.getParent(); - } - } + /** Starts surface and attaches the root view. */ + @AnyThread + public void startSurface( + final int surfaceId, @NonNull final View rootView, ThemedReactContext themedReactContext) { + SurfaceMountingManager mountingManager = startSurface(surfaceId); + mountingManager.attachRootView(rootView, themedReactContext); } /** - * This mutates the rootView, which is an Android View, so this should only be called on the UI - * thread. - * - * @param reactRootTag - * @param rootView + * Starts surface without attaching the view. All view operations executed against that surface + * will be queued until the view is attached. */ - @ThreadConfined(UI) - public void addRootView(int reactRootTag, @NonNull View rootView) { - if (rootView.getId() != View.NO_ID) { - throw new IllegalViewOperationException( - "Trying to add a root view with an explicit id already set. React Native uses " - + "the id field to track react tags and will overwrite this field. If that is fine, " - + "explicitly overwrite the id field to View.NO_ID before calling addRootView."); + @AnyThread + public SurfaceMountingManager startSurface(final int surfaceId) { + SurfaceMountingManager surfaceMountingManager = + new SurfaceMountingManager( + surfaceId, + mJSResponderHandler, + mViewManagerRegistry, + mRootViewManager, + mMountItemExecutor); + + // There could technically be a race condition here if addRootView is called twice from + // different threads, though this is (probably) extremely unlikely, and likely an error. + // This logic to protect against race conditions is a holdover from older code, and we don't + // know if it actually happens in practice - so, we're logging soft exceptions for now. + // This *will* crash in Debug mode, but not in production. + mSurfaceIdToManager.putIfAbsent(surfaceId, surfaceMountingManager); + if (mSurfaceIdToManager.get(surfaceId) != surfaceMountingManager) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Called startSurface more than once for the SurfaceId [" + surfaceId + "]")); } - mTagToViewState.put( - reactRootTag, new ViewState(reactRootTag, rootView, mRootViewManager, true)); - rootView.setId(reactRootTag); + mMostRecentSurfaceMountingManager = mSurfaceIdToManager.get(surfaceId); + return surfaceMountingManager; } - /** Delete rootView and all children/ */ - @UiThread - public void deleteRootView(int reactRootTag) { - if (mTagToViewState.containsKey(reactRootTag)) { - dropView(mTagToViewState.get(reactRootTag).mView, true); + @AnyThread + public void attachRootView( + final int surfaceId, @NonNull final View rootView, ThemedReactContext themedReactContext) { + SurfaceMountingManager surfaceMountingManager = + getSurfaceManagerEnforced(surfaceId, "attachView"); + + if (surfaceMountingManager.isStopped()) { + ReactSoftExceptionLogger.logSoftException( + TAG, new IllegalStateException("Trying to attach a view to a stopped surface")); + return; } - } - - /** Releases all references to given native View. */ - @UiThread - private void dropView(@NonNull View view, boolean deleteImmediately) { - UiThreadUtil.assertOnUiThread(); - final int reactTag = view.getId(); - ViewState state = getViewState(reactTag); - ViewManager viewManager = state.mViewManager; + surfaceMountingManager.attachRootView(rootView, themedReactContext); + } - if (!state.mIsRoot && viewManager != null) { - // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} - viewManager.onDropViewInstance(view); - } - if (view instanceof ViewGroup && viewManager instanceof ViewGroupManager) { - final ViewGroup viewGroup = (ViewGroup) view; - final ViewGroupManager viewGroupManager = getViewGroupManager(state); - - // As documented elsewhere, sometimes when a child is removed from a parent, that change - // is not immediately available in the hierarchy until a future UI tick. This can cause - // inconsistent child counts, etc, but it can _also_ cause us to drop views that shouldn't, - // because they're removed from the parent but that change isn't immediately visible. So, - // we do two things: 1) delay this logic until the next UI thread tick, 2) ignore children - // who don't report the expected parent. - // For most cases, we _do not_ want this logic to run, anyway, since it either means that we - // don't have a correct set of MountingInstructions; or it means that we're tearing down an - // entire screen, in which case we can safely delete everything immediately, not having - // executed any remove instructions immediately before this. - if (deleteImmediately) { - dropChildren(reactTag, viewGroup, viewGroupManager); - } else { - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - dropChildren(reactTag, viewGroup, viewGroupManager); - } - }); + @AnyThread + public void stopSurface(final int surfaceId) { + SurfaceMountingManager surfaceMountingManager = mSurfaceIdToManager.get(surfaceId); + if (surfaceMountingManager != null) { + // Maximum number of stopped surfaces to keep track of + while (mStoppedSurfaceIds.size() >= MAX_STOPPED_SURFACE_IDS_LENGTH) { + Integer staleStoppedId = mStoppedSurfaceIds.get(0); + mSurfaceIdToManager.remove(staleStoppedId.intValue()); + mStoppedSurfaceIds.remove(staleStoppedId); + FLog.d(TAG, "Removing stale SurfaceMountingManager: [%d]", staleStoppedId.intValue()); } - } + mStoppedSurfaceIds.add(surfaceId); - mTagToViewState.remove(reactTag); - } + surfaceMountingManager.stopSurface(); - @UiThread - private void dropChildren( - int reactTag, - @NonNull ViewGroup viewGroup, - @NonNull ViewGroupManager viewGroupManager) { - for (int i = viewGroupManager.getChildCount(viewGroup) - 1; i >= 0; i--) { - View child = viewGroupManager.getChildAt(viewGroup, i); - if (getNullableViewState(child.getId()) != null) { - if (SHOW_CHANGED_VIEW_HIERARCHIES) { - FLog.e( - TAG, - "Automatically dropping view that is still attached to a parent being dropped. Parent: [" - + reactTag - + "] child: [" - + child.getId() - + "]"); - } - ViewParent childParent = child.getParent(); - if (childParent == null || !childParent.equals(viewGroup)) { - int childParentId = - (childParent == null - ? -1 - : (childParent instanceof ViewGroup ? ((ViewGroup) childParent).getId() : -1)); - FLog.e( - TAG, - "Recursively deleting children of [" - + reactTag - + "] but parent of child [" - + child.getId() - + "] is [" - + childParentId - + "]"); - } else { - dropView(child, true); - } + if (surfaceMountingManager == mMostRecentSurfaceMountingManager) { + mMostRecentSurfaceMountingManager = null; } - viewGroupManager.removeViewAt(viewGroup, i); + } else { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Cannot call stopSurface on non-existent surface: [" + surfaceId + "]")); } } - @UiThread - public void addViewAt(final int parentTag, final int tag, final int index) { - UiThreadUtil.assertOnUiThread(); - ViewState parentViewState = getViewState(parentTag); - if (!(parentViewState.mView instanceof ViewGroup)) { - String message = - "Unable to add a view into a view that is not a ViewGroup. ParentTag: " - + parentTag - + " - Tag: " - + tag - + " - Index: " - + index; - FLog.e(TAG, message); - throw new IllegalStateException(message); - } - final ViewGroup parentView = (ViewGroup) parentViewState.mView; - ViewState viewState = getViewState(tag); - final View view = viewState.mView; - if (view == null) { - throw new IllegalStateException( - "Unable to find view for viewState " + viewState + " and tag " + tag); - } - - // Display children before inserting - if (SHOW_CHANGED_VIEW_HIERARCHIES) { - FLog.e(TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE"); - logViewHierarchy(parentView, false); + @Nullable + public SurfaceMountingManager getSurfaceManager(int surfaceId) { + if (mLastQueriedSurfaceMountingManager != null + && mLastQueriedSurfaceMountingManager.getSurfaceId() == surfaceId) { + return mLastQueriedSurfaceMountingManager; } - try { - getViewGroupManager(parentViewState).addView(parentView, view, index); - } catch (IllegalStateException e) { - // Wrap error with more context for debugging - throw new IllegalStateException( - "addViewAt: failed to insert view [" - + tag - + "] into parent [" - + parentTag - + "] at index " - + index, - e); + if (mMostRecentSurfaceMountingManager != null + && mMostRecentSurfaceMountingManager.getSurfaceId() == surfaceId) { + return mMostRecentSurfaceMountingManager; } - // Display children after inserting - if (SHOW_CHANGED_VIEW_HIERARCHIES) { - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - FLog.e( - TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " AFTER"); - logViewHierarchy(parentView, false); - } - }); - } + SurfaceMountingManager surfaceMountingManager = mSurfaceIdToManager.get(surfaceId); + mLastQueriedSurfaceMountingManager = surfaceMountingManager; + return surfaceMountingManager; } @NonNull - private ViewState getViewState(int tag) { - ViewState viewState = mTagToViewState.get(tag); - if (viewState == null) { - throw new RetryableMountingLayerException("Unable to find viewState view for tag " + tag); - } - return viewState; - } - - public boolean getViewExists(int tag) { - return mTagToViewState.get(tag) != null; - } - - private @Nullable ViewState getNullableViewState(int tag) { - return mTagToViewState.get(tag); - } - - @Deprecated - public void receiveCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs) { - ViewState viewState = getNullableViewState(reactTag); - - // It's not uncommon for JS to send events as/after a component is being removed from the - // view hierarchy. For example, TextInput may send a "blur" command in response to the view - // disappearing. Throw `ReactNoCrashSoftException` so they're logged but don't crash in dev - // for now. - if (viewState == null) { - throw new RetryableMountingLayerException( - "Unable to find viewState for tag: " + reactTag + " for commandId: " + commandId); - } + public SurfaceMountingManager getSurfaceManagerEnforced(int surfaceId, String context) { + SurfaceMountingManager surfaceMountingManager = getSurfaceManager(surfaceId); - if (viewState.mViewManager == null) { - throw new RetryableMountingLayerException("Unable to find viewManager for tag " + reactTag); - } - - if (viewState.mView == null) { + if (surfaceMountingManager == null) { throw new RetryableMountingLayerException( - "Unable to find viewState view for tag " + reactTag); + "Unable to find SurfaceMountingManager for surfaceId: [" + + surfaceId + + "]. Context: " + + context); } - viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs); + return surfaceMountingManager; } - public void receiveCommand( - int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) { - ViewState viewState = getNullableViewState(reactTag); - - // It's not uncommon for JS to send events as/after a component is being removed from the - // view hierarchy. For example, TextInput may send a "blur" command in response to the view - // disappearing. Throw `ReactNoCrashSoftException` so they're logged but don't crash in dev - // for now. - if (viewState == null) { - throw new RetryableMountingLayerException( - "Unable to find viewState for tag: " + reactTag + " for commandId: " + commandId); - } - - if (viewState.mViewManager == null) { - throw new RetryableMountingLayerException( - "Unable to find viewState manager for tag " + reactTag); + public boolean surfaceIsStopped(int surfaceId) { + if (mStoppedSurfaceIds.contains(surfaceId)) { + return true; } - if (viewState.mView == null) { - throw new RetryableMountingLayerException( - "Unable to find viewState view for tag " + reactTag); + SurfaceMountingManager surfaceMountingManager = getSurfaceManager(surfaceId); + if (surfaceMountingManager != null && surfaceMountingManager.isStopped()) { + return true; } - viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs); + return false; } - public void sendAccessibilityEvent(int reactTag, int eventType) { - ViewState viewState = getViewState(reactTag); - - if (viewState.mViewManager == null) { - throw new RetryableMountingLayerException( - "Unable to find viewState manager for tag " + reactTag); + public boolean isWaitingForViewAttach(int surfaceId) { + SurfaceMountingManager mountingManager = getSurfaceManager(surfaceId); + if (mountingManager == null) { + return false; } - if (viewState.mView == null) { - throw new RetryableMountingLayerException( - "Unable to find viewState view for tag " + reactTag); + if (mountingManager.isStopped()) { + return false; } - viewState.mView.sendAccessibilityEvent(eventType); - } - - @SuppressWarnings("unchecked") // prevents unchecked conversion warn of the type - private static @NonNull ViewGroupManager getViewGroupManager( - @NonNull ViewState viewState) { - if (viewState.mViewManager == null) { - throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); - } - return (ViewGroupManager) viewState.mViewManager; + return !mountingManager.isRootViewAttached(); } - @UiThread - public void removeViewAt(final int tag, final int parentTag, int index) { - UiThreadUtil.assertOnUiThread(); - ViewState viewState = getNullableViewState(parentTag); - - if (viewState == null) { - ReactSoftException.logSoftException( - MountingManager.TAG, - new IllegalStateException( - "Unable to find viewState for tag: " + parentTag + " for removeViewAt")); - return; - } - - final ViewGroup parentView = (ViewGroup) viewState.mView; - - if (parentView == null) { - throw new IllegalStateException("Unable to find view for tag " + parentTag); - } - - if (SHOW_CHANGED_VIEW_HIERARCHIES) { - // Display children before deleting any - FLog.e(TAG, "removeViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE"); - logViewHierarchy(parentView, false); - } - - ViewGroupManager viewGroupManager = getViewGroupManager(viewState); - - // Verify that the view we're about to remove has the same tag we expect - View view = viewGroupManager.getChildAt(parentView, index); - int actualTag = (view != null ? view.getId() : -1); - if (actualTag != tag) { - int tagActualIndex = -1; - int parentChildrenCount = parentView.getChildCount(); - for (int i = 0; i < parentChildrenCount; i++) { - if (parentView.getChildAt(i).getId() == tag) { - tagActualIndex = i; - break; + /** + * Get SurfaceMountingManager associated with a ReactTag. Unfortunately, this requires lookups + * over N maps, where N is the number of active or recently-stopped Surfaces. Each lookup will + * cost `log(M)` operations where M is the number of reactTags in the surface, so the total cost + * per lookup is `O(N * log(M))`. + * + *

To mitigate this cost, we attempt to keep track of the "most recent" SurfaceMountingManager + * and do lookups in it first. For the vast majority of use-cases, except for events or operations + * sent to off-screen surfaces, or use-cases where multiple surfaces are visible and interactable, + * this will reduce the lookup time to `O(log(M))`. Someone smarter than me could probably figure + * out an amortized time. + * + * @param reactTag + * @return + */ + @Nullable + public SurfaceMountingManager getSurfaceManagerForView(int reactTag) { + if (mMostRecentSurfaceMountingManager != null + && mMostRecentSurfaceMountingManager.getViewExists(reactTag)) { + return mMostRecentSurfaceMountingManager; + } + + for (Map.Entry entry : mSurfaceIdToManager.entrySet()) { + SurfaceMountingManager smm = entry.getValue(); + if (smm != mMostRecentSurfaceMountingManager && smm.getViewExists(reactTag)) { + if (mMostRecentSurfaceMountingManager == null) { + mMostRecentSurfaceMountingManager = smm; } + return smm; } - - // TODO T74425739: previously, we did not do this check and `removeViewAt` would be executed - // below, sometimes crashing there. *However*, interestingly enough, `removeViewAt` would not - // complain if you removed views from an already-empty parent. This seems necessary currently - // for certain ViewManagers that remove their own children - like BottomSheet? - // This workaround seems not-great, but for now, we just return here for - // backwards-compatibility. Essentially, if a view has already been removed from the - // hierarchy, we treat it as a noop. - if (tagActualIndex == -1) { - FLog.e( - TAG, - "removeViewAt: [" - + tag - + "] -> [" - + parentTag - + "] @" - + index - + ": view already removed from parent! Children in parent: " - + parentChildrenCount); - return; - } - - // Here we are guaranteed that the view is still in the View hierarchy, just - // at a different index. In debug mode we'll crash here; in production, we'll remove - // the child from the parent and move on. - // This is an issue that is safely recoverable 95% of the time. If this allows corruption - // of the view hierarchy and causes bugs or a crash after this point, there will be logs - // indicating that this happened. - // This is likely *only* necessary because of Fabric's LayoutAnimations implementation. - // If we can fix the bug there, or remove the need for LayoutAnimation index adjustment - // entirely, we can just throw this exception without regression user experience. - logViewHierarchy(parentView, true); - ReactSoftException.logSoftException( - TAG, - new IllegalStateException( - "Tried to remove view [" - + tag - + "] of parent [" - + parentTag - + "] at index " - + index - + ", but got view tag " - + actualTag - + " - actual index of view: " - + tagActualIndex)); - index = tagActualIndex; - } - - try { - viewGroupManager.removeViewAt(parentView, index); - } catch (RuntimeException e) { - // Note: `getChildCount` may not always be accurate! - // We don't currently have a good explanation other than, in situations where you - // would empirically expect to see childCount > 0, the childCount is reported as 0. - // This is likely due to a ViewManager overriding getChildCount or some other methods - // in a way that is strictly incorrect, but potentially only visible here. - // The failure mode is actually that in `removeViewAt`, a NullPointerException is - // thrown when we try to perform an operation on a View that doesn't exist, and - // is therefore null. - // We try to add some extra diagnostics here, but we always try to remove the View - // from the hierarchy first because detecting by looking at childCount will not work. - // - // Note that the lesson here is that `getChildCount` is not /required/ to adhere to - // any invariants. If you add 9 children to a parent, the `getChildCount` of the parent - // may not be equal to 9. This apparently causes no issues with Android and is common - // enough that we shouldn't try to change this invariant, without a lot of thought. - int childCount = viewGroupManager.getChildCount(parentView); - - logViewHierarchy(parentView, true); - - throw new IllegalStateException( - "Cannot remove child at index " - + index - + " from parent ViewGroup [" - + parentView.getId() - + "], only " - + childCount - + " children in parent. Warning: childCount may be incorrect!", - e); - } - - // Display children after deleting any - if (SHOW_CHANGED_VIEW_HIERARCHIES) { - final int finalIndex = index; - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - FLog.e( - TAG, - "removeViewAt: [" - + tag - + "] -> [" - + parentTag - + "] idx: " - + finalIndex - + " AFTER"); - logViewHierarchy(parentView, false); - } - }); } + return null; } - @UiThread - public void createView( - @NonNull ThemedReactContext themedReactContext, - @NonNull String componentName, - int reactTag, - @Nullable ReadableMap props, - @Nullable StateWrapper stateWrapper, - boolean isLayoutable) { - if (getNullableViewState(reactTag) != null) { - return; - } - - View view = null; - ViewManager viewManager = null; - - ReactStylesDiffMap propsDiffMap = null; - if (props != null) { - propsDiffMap = new ReactStylesDiffMap(props); - } + @NonNull + @AnyThread + public SurfaceMountingManager getSurfaceManagerForViewEnforced(int reactTag) { + SurfaceMountingManager surfaceMountingManager = getSurfaceManagerForView(reactTag); - if (isLayoutable) { - viewManager = mViewManagerRegistry.get(componentName); - // View Managers are responsible for dealing with initial state and props. - view = - viewManager.createView( - themedReactContext, propsDiffMap, stateWrapper, mJSResponderHandler); - view.setId(reactTag); + if (surfaceMountingManager == null) { + throw new RetryableMountingLayerException( + "Unable to find SurfaceMountingManager for tag: [" + reactTag + "]"); } - ViewState viewState = new ViewState(reactTag, view, viewManager); - viewState.mCurrentProps = propsDiffMap; - viewState.mCurrentState = (stateWrapper != null ? stateWrapper.getState() : null); - - mTagToViewState.put(reactTag, viewState); + return surfaceMountingManager; } - @UiThread - public void updateProps(int reactTag, @Nullable ReadableMap props) { - if (props == null) { - return; - } - UiThreadUtil.assertOnUiThread(); - ViewState viewState = getViewState(reactTag); - viewState.mCurrentProps = new ReactStylesDiffMap(props); - View view = viewState.mView; - - if (view == null) { - throw new IllegalStateException("Unable to find view for tag " + reactTag); - } - - Assertions.assertNotNull(viewState.mViewManager) - .updateProperties(view, viewState.mCurrentProps); + public boolean getViewExists(int reactTag) { + return getSurfaceManagerForView(reactTag) != null; } - @UiThread - public void updateLayout(int reactTag, int x, int y, int width, int height) { + @Deprecated + public void receiveCommand( + int surfaceId, int reactTag, int commandId, @Nullable ReadableArray commandArgs) { UiThreadUtil.assertOnUiThread(); - - ViewState viewState = getViewState(reactTag); - // Do not layout Root Views - if (viewState.mIsRoot) { - return; - } - - View viewToUpdate = viewState.mView; - if (viewToUpdate == null) { - throw new IllegalStateException("Unable to find View for tag: " + reactTag); - } - - viewToUpdate.measure( - View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); - - ViewParent parent = viewToUpdate.getParent(); - if (parent instanceof RootView) { - parent.requestLayout(); - } - - // TODO: T31905686 Check if the parent of the view has to layout the view, or the child has - // to lay itself out. see NativeViewHierarchyManager.updateLayout - viewToUpdate.layout(x, y, x + width, y + height); + getSurfaceManagerEnforced(surfaceId, "receiveCommand:int") + .receiveCommand(reactTag, commandId, commandArgs); } - @UiThread - public void updatePadding(int reactTag, int left, int top, int right, int bottom) { + public void receiveCommand( + int surfaceId, int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) { UiThreadUtil.assertOnUiThread(); - - ViewState viewState = getViewState(reactTag); - // Do not layout Root Views - if (viewState.mIsRoot) { - return; - } - - View viewToUpdate = viewState.mView; - if (viewToUpdate == null) { - throw new IllegalStateException("Unable to find View for tag: " + reactTag); - } - - ViewManager viewManager = viewState.mViewManager; - if (viewManager == null) { - throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); - } - - //noinspection unchecked - viewManager.setPadding(viewToUpdate, left, top, right, bottom); + getSurfaceManagerEnforced(surfaceId, "receiveCommand:string") + .receiveCommand(reactTag, commandId, commandArgs); } - @UiThread - public void deleteView(int reactTag) { + /** + * Send an accessibility eventType to a Native View. eventType is any valid `AccessibilityEvent.X` + * value. + * + *

Why accept `-1` SurfaceId? Currently there are calls to UIManager.sendAccessibilityEvent + * which is a legacy API and accepts only reactTag. We will have to investigate and migrate away + * from those calls over time. + * + * @param surfaceId {@link int} that identifies the surface or -1 to temporarily support backward + * compatibility. + * @param reactTag {@link int} that identifies the react Tag of the view. + * @param eventType {@link int} that identifies Android eventType. see {@link + * View#sendAccessibilityEvent} + */ + public void sendAccessibilityEvent(int surfaceId, int reactTag, int eventType) { UiThreadUtil.assertOnUiThread(); - ViewState viewState = getNullableViewState(reactTag); - - if (viewState == null) { - ReactSoftException.logSoftException( - MountingManager.TAG, - new IllegalStateException( - "Unable to find viewState for tag: " + reactTag + " for deleteView")); - return; - } - - // To delete we simply remove the tag from the registry. - // In the past we called dropView here, but we want to rely on either - // (1) the correct set of MountInstructions being sent to the platform - // and/or (2) dropView being called by stopSurface. - // If Views are orphaned at this stage and leaked, it's a problem in - // the differ or LayoutAnimations, not MountingManager. - // Additionally, as documented in `dropView`, we cannot always trust a - // view's children to be up-to-date. - mTagToViewState.remove(reactTag); - - // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} - ViewManager viewManager = viewState.mViewManager; - if (!viewState.mIsRoot && viewManager != null) { - viewManager.onDropViewInstance(viewState.mView); + if (surfaceId == View.NO_ID) { + getSurfaceManagerForViewEnforced(reactTag).sendAccessibilityEvent(reactTag, eventType); + } else { + getSurfaceManagerEnforced(surfaceId, "sendAccessibilityEvent") + .sendAccessibilityEvent(reactTag, eventType); } } @UiThread - public void updateState(final int reactTag, @Nullable StateWrapper stateWrapper) { + public void updateProps(int reactTag, @Nullable ReadableMap props) { UiThreadUtil.assertOnUiThread(); - ViewState viewState = getViewState(reactTag); - @Nullable ReadableNativeMap newState = stateWrapper == null ? null : stateWrapper.getState(); - - viewState.mCurrentState = newState; - - ViewManager viewManager = viewState.mViewManager; - - if (viewManager == null) { - throw new IllegalStateException("Unable to find ViewManager for tag: " + reactTag); - } - Object extraData = - viewManager.updateState(viewState.mView, viewState.mCurrentProps, stateWrapper); - if (extraData != null) { - viewManager.updateExtraData(viewState.mView, extraData); - } - } - - @UiThread - public void preallocateView( - @NonNull ThemedReactContext reactContext, - String componentName, - int reactTag, - @Nullable ReadableMap props, - @Nullable StateWrapper stateWrapper, - boolean isLayoutable) { - - if (getNullableViewState(reactTag) != null) { - throw new IllegalStateException( - "View for component " + componentName + " with tag " + reactTag + " already exists."); + if (props == null) { + return; } - createView(reactContext, componentName, reactTag, props, stateWrapper, isLayoutable); - } - - @UiThread - public void updateEventEmitter(int reactTag, @NonNull EventEmitterWrapper eventEmitter) { - UiThreadUtil.assertOnUiThread(); - ViewState viewState = mTagToViewState.get(reactTag); - if (viewState == null) { - // TODO T62717437 - Use a flag to determine that these event emitters belong to virtual nodes - // only. - viewState = new ViewState(reactTag, null, null); - mTagToViewState.put(reactTag, viewState); - } - viewState.mEventEmitter = eventEmitter; + getSurfaceManagerForViewEnforced(reactTag).updateProps(reactTag, props); } /** - * Set the JS responder for the view associated with the tags received as a parameter. - * - *

The JSResponder coordinates the return values of the onInterceptTouch method in Android - * Views. This allows JS to coordinate when a touch should be handled by JS or by the Android - * native views. See {@link JSResponderHandler} for more details. - * - *

This method is going to be executed on the UIThread as soon as it is delivered from JS to - * RN. - * - *

Currently, there is no warranty that the view associated with the react tag exists, because - * this method is not handled by the react commit process. - * - * @param reactTag React tag of the first parent of the view that is NOT virtual - * @param initialReactTag React tag of the JS view that initiated the touch operation - * @param blockNativeResponder If native responder should be blocked or not + * Clears the JS Responder specified by {@link #setJSResponder(int, int, int, boolean)}. After + * this method is called, all the touch events are going to be handled by JS. */ @UiThread - public synchronized void setJSResponder( - int reactTag, int initialReactTag, boolean blockNativeResponder) { - if (!blockNativeResponder) { - mJSResponderHandler.setJSResponder(initialReactTag, null); - return; - } - - ViewState viewState = getViewState(reactTag); - View view = viewState.mView; - if (initialReactTag != reactTag && view instanceof ViewParent) { - // In this case, initialReactTag corresponds to a virtual/layout-only View, and we already - // have a parent of that View in reactTag, so we can use it. - mJSResponderHandler.setJSResponder(initialReactTag, (ViewParent) view); - return; - } else if (view == null) { - SoftAssertions.assertUnreachable("Cannot find view for tag " + reactTag + "."); - return; - } + public void clearJSResponder() { + // MountingManager and SurfaceMountingManagers all share the same JSResponderHandler. + // Must be called on MountingManager instead of SurfaceMountingManager, because we don't + // know what surfaceId it's being called for. + mJSResponderHandler.clearJSResponder(); + } - if (viewState.mIsRoot) { - SoftAssertions.assertUnreachable( - "Cannot block native responder on " + reactTag + " that is a root view"); + @AnyThread + @ThreadConfined(ANY) + public @Nullable EventEmitterWrapper getEventEmitter(int surfaceId, int reactTag) { + SurfaceMountingManager surfaceMountingManager = + (surfaceId == -1 ? getSurfaceManagerForView(reactTag) : getSurfaceManager(surfaceId)); + if (surfaceMountingManager == null) { + return null; } - mJSResponderHandler.setJSResponder(initialReactTag, view.getParent()); + return surfaceMountingManager.getEventEmitter(reactTag); } /** - * Clears the JS Responder specified by {@link #setJSResponder(int, int, boolean)}. After this - * method is called, all the touch events are going to be handled by JS. + * Measure a component, given localData, props, state, and measurement information. This needs to + * remain here for now - and not in SurfaceMountingManager - because sometimes measures are made + * outside of the context of a Surface; especially from C++ before StartSurface is called. + * + * @param context + * @param componentName + * @param localData + * @param props + * @param state + * @param width + * @param widthMode + * @param height + * @param heightMode + * @param attachmentsPositions + * @return */ - @UiThread - public void clearJSResponder() { - mJSResponderHandler.clearJSResponder(); - } - @AnyThread public long measure( - @NonNull Context context, + @NonNull ReactContext context, @NonNull String componentName, @NonNull ReadableMap localData, @NonNull ReadableMap props, @@ -764,53 +380,50 @@ public long measure( attachmentsPositions); } - @AnyThread - @ThreadConfined(ANY) - public @Nullable EventEmitterWrapper getEventEmitter(int reactTag) { - ViewState viewState = getNullableViewState(reactTag); - return viewState == null ? null : viewState.mEventEmitter; - } - /** - * This class holds view state for react tags. Objects of this class are stored into the {@link - * #mTagToViewState}, and they should be updated in the same thread. + * Measure a component, given localData, props, state, and measurement information. This needs to + * remain here for now - and not in SurfaceMountingManager - because sometimes measures are made + * outside of the context of a Surface; especially from C++ before StartSurface is called. + * + * @param context + * @param componentName + * @param attributedString + * @param paragraphAttributes + * @param width + * @param widthMode + * @param height + * @param heightMode + * @param attachmentsPositions + * @return */ - private static class ViewState { - @Nullable final View mView; - final int mReactTag; - final boolean mIsRoot; - @Nullable final ViewManager mViewManager; - @Nullable public ReactStylesDiffMap mCurrentProps = null; - @Nullable public ReadableMap mCurrentLocalData = null; - @Nullable public ReadableMap mCurrentState = null; - @Nullable public EventEmitterWrapper mEventEmitter = null; - - private ViewState(int reactTag, @Nullable View view, @Nullable ViewManager viewManager) { - this(reactTag, view, viewManager, false); - } + @AnyThread + public long measureTextMapBuffer( + @NonNull ReactContext context, + @NonNull String componentName, + @NonNull ReadableMapBuffer attributedString, + @NonNull ReadableMapBuffer paragraphAttributes, + float width, + @NonNull YogaMeasureMode widthMode, + float height, + @NonNull YogaMeasureMode heightMode, + @Nullable float[] attachmentsPositions) { - private ViewState(int reactTag, @Nullable View view, ViewManager viewManager, boolean isRoot) { - mReactTag = reactTag; - mView = view; - mIsRoot = isRoot; - mViewManager = viewManager; - } + return TextLayoutManagerMapBuffer.measureText( + context, + attributedString, + paragraphAttributes, + width, + widthMode, + height, + heightMode, + new ReactTextViewManagerCallback() { + @Override + public void onPostProcessSpannable(Spannable text) {} + }, + attachmentsPositions); + } - @Override - public String toString() { - boolean isLayoutOnly = mViewManager == null; - return "ViewState [" - + mReactTag - + "] - isRoot: " - + mIsRoot - + " - props: " - + mCurrentProps - + " - localData: " - + mCurrentLocalData - + " - viewManager: " - + mViewManager - + " - isLayoutOnly: " - + isLayoutOnly; - } + public void initializeViewManager(String componentName) { + mViewManagerRegistry.get(componentName); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java new file mode 100644 index 0000000000000..737a29559aab5 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java @@ -0,0 +1,1023 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.fabric.mounting; + +import static com.facebook.infer.annotation.ThreadConfined.ANY; +import static com.facebook.infer.annotation.ThreadConfined.UI; + +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.annotation.UiThread; +import com.facebook.common.logging.FLog; +import com.facebook.infer.annotation.Assertions; +import com.facebook.infer.annotation.ThreadConfined; +import com.facebook.react.bridge.ReactNoCrashSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.RetryableMountingLayerException; +import com.facebook.react.bridge.SoftAssertions; +import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.config.ReactFeatureFlags; +import com.facebook.react.fabric.events.EventEmitterWrapper; +import com.facebook.react.fabric.mounting.MountingManager.MountItemExecutor; +import com.facebook.react.fabric.mounting.mountitems.MountItem; +import com.facebook.react.touch.JSResponderHandler; +import com.facebook.react.uimanager.IllegalViewOperationException; +import com.facebook.react.uimanager.ReactRoot; +import com.facebook.react.uimanager.ReactStylesDiffMap; +import com.facebook.react.uimanager.RootView; +import com.facebook.react.uimanager.RootViewManager; +import com.facebook.react.uimanager.StateWrapper; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManager; +import com.facebook.react.uimanager.ViewManagerRegistry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import javax.annotation.Nullable; + +public class SurfaceMountingManager { + public static final String TAG = SurfaceMountingManager.class.getSimpleName(); + + private static final boolean SHOW_CHANGED_VIEW_HIERARCHIES = ReactBuildConfig.DEBUG && false; + + private volatile boolean mIsStopped = false; + private volatile boolean mRootViewAttached = false; + + @Nullable private ThemedReactContext mThemedReactContext; + + // These are all non-null, until StopSurface is called + private ConcurrentHashMap mTagToViewState = + new ConcurrentHashMap<>(); // any thread + private ConcurrentLinkedQueue mOnViewAttachItems = new ConcurrentLinkedQueue<>(); + private JSResponderHandler mJSResponderHandler; + private ViewManagerRegistry mViewManagerRegistry; + private RootViewManager mRootViewManager; + private MountItemExecutor mMountItemExecutor; + + // This is null *until* StopSurface is called. + private Set mTagSetForStoppedSurface; + + private final int mSurfaceId; + + public SurfaceMountingManager( + int surfaceId, + @NonNull JSResponderHandler jsResponderHandler, + @NonNull ViewManagerRegistry viewManagerRegistry, + @NonNull RootViewManager rootViewManager, + @NonNull MountItemExecutor mountItemExecutor) { + mSurfaceId = surfaceId; + + mJSResponderHandler = jsResponderHandler; + mViewManagerRegistry = viewManagerRegistry; + mRootViewManager = rootViewManager; + mMountItemExecutor = mountItemExecutor; + } + + public boolean isStopped() { + return mIsStopped; + } + + public void attachRootView(View rootView, ThemedReactContext themedReactContext) { + mThemedReactContext = themedReactContext; + addRootView(rootView); + } + + public int getSurfaceId() { + return mSurfaceId; + } + + public boolean isRootViewAttached() { + return mRootViewAttached; + } + + @Nullable + public ThemedReactContext getContext() { + return mThemedReactContext; + } + + private static void logViewHierarchy(ViewGroup parent, boolean recurse) { + int parentTag = parent.getId(); + FLog.e(TAG, " "); + for (int i = 0; i < parent.getChildCount(); i++) { + FLog.e( + TAG, + " "); + } + FLog.e(TAG, " "); + + if (recurse) { + FLog.e(TAG, "Displaying Ancestors:"); + ViewParent ancestor = parent.getParent(); + while (ancestor != null) { + ViewGroup ancestorViewGroup = (ancestor instanceof ViewGroup ? (ViewGroup) ancestor : null); + int ancestorId = ancestorViewGroup == null ? View.NO_ID : ancestorViewGroup.getId(); + FLog.e( + TAG, + ""); + ancestor = ancestor.getParent(); + } + } + } + + public boolean getViewExists(int tag) { + // If Surface stopped, check if tag *was* associated with this Surface, even though it's been + // deleted. This helps distinguish between scenarios where an invalid tag is referenced, vs + // race conditions where an imperative method is called on a tag during/just after StopSurface. + if (mTagSetForStoppedSurface != null && mTagSetForStoppedSurface.contains(tag)) { + return true; + } + if (mTagToViewState == null) { + return false; + } + return mTagToViewState.containsKey(tag); + } + + @AnyThread + public void executeOnViewAttach(MountItem item) { + mOnViewAttachItems.add(item); + } + + @AnyThread + private void addRootView(@NonNull final View rootView) { + if (isStopped()) { + return; + } + + mTagToViewState.put(mSurfaceId, new ViewState(mSurfaceId, rootView, mRootViewManager, true)); + + Runnable runnable = + new Runnable() { + @Override + public void run() { + // The CPU has ticked since `addRootView` was called, so the surface could technically + // have already stopped here. + if (isStopped()) { + return; + } + + if (rootView.getId() == mSurfaceId) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalViewOperationException( + "Race condition in addRootView detected. Trying to set an id of [" + + mSurfaceId + + "] on the RootView, but that id has already been set. ")); + } else if (rootView.getId() != View.NO_ID) { + FLog.e( + TAG, + "Trying to add RootTag to RootView that already has a tag: existing tag: [%d] new tag: [%d]", + rootView.getId(), + mSurfaceId); + throw new IllegalViewOperationException( + "Trying to add a root view with an explicit id already set. React Native uses " + + "the id field to track react tags and will overwrite this field. If that is fine, " + + "explicitly overwrite the id field to View.NO_ID before calling addRootView."); + } + rootView.setId(mSurfaceId); + + if (rootView instanceof ReactRoot) { + ((ReactRoot) rootView).setRootViewTag(mSurfaceId); + } + mRootViewAttached = true; + + executeViewAttachMountItems(); + } + }; + + if (UiThreadUtil.isOnUiThread()) { + runnable.run(); + } else { + UiThreadUtil.runOnUiThread(runnable); + } + } + + @UiThread + @ThreadConfined(UI) + private void executeViewAttachMountItems() { + mMountItemExecutor.executeItems(mOnViewAttachItems); + } + + /** + * Stop surface and all operations within it. Garbage-collect Views (caller is responsible for + * removing RootView from View layer). + * + *

Delete rootView from cache. Since RN does not control the RootView, in a sense, the fragment + * is responsible for actually removing the RootView from the hierarchy / tearing down the + * fragment. + * + *

In the original version(s) of this function, we recursively went through all children of the + * View and dropped those Views as well; ad infinitum. This was before we had a + * SurfaceMountingManager, and all tags were in one global map. Doing this was particularly + * important in the case of StopSurface, where race conditions between threads meant you couldn't + * rely on DELETE instructions actually deleting all Views in the Surface. + * + *

Now that we have SurfaceMountingManager, we can simply drop our local reference to the View. + * Since it will be removed from the View hierarchy entirely (outside of the scope of this class), + * garbage collection will take care of destroying it and all descendents. + */ + @AnyThread + public void stopSurface() { + if (isStopped()) { + return; + } + + // Prevent more views from being created, or the hierarchy from being manipulated at all. This + // causes further operations to noop. + mIsStopped = true; + + // Reset all StateWrapper objects + // Since this can happen on any thread, is it possible to race between StateWrapper destruction + // and some accesses from View classes in the UI thread? + for (ViewState viewState : mTagToViewState.values()) { + if (viewState.mStateWrapper != null) { + viewState.mStateWrapper.destroyState(); + viewState.mStateWrapper = null; + } + if (ReactFeatureFlags.enableAggressiveEventEmitterCleanup) { + if (viewState.mEventEmitter != null) { + viewState.mEventEmitter.destroy(); + viewState.mEventEmitter = null; + } + } + } + + Runnable runnable = + new Runnable() { + @Override + public void run() { + // We must call `onDropViewInstance` on all remaining Views + for (ViewState viewState : mTagToViewState.values()) { + onViewStateDeleted(viewState); + } + + // Evict all views from cache and memory + mTagSetForStoppedSurface = mTagToViewState.keySet(); + mTagToViewState = null; + mJSResponderHandler = null; + mRootViewManager = null; + mMountItemExecutor = null; + mOnViewAttachItems.clear(); + } + }; + + if (UiThreadUtil.isOnUiThread()) { + runnable.run(); + } else { + UiThreadUtil.runOnUiThread(runnable); + } + } + + @UiThread + public void addViewAt(final int parentTag, final int tag, final int index) { + UiThreadUtil.assertOnUiThread(); + if (isStopped()) { + return; + } + + ViewState parentViewState = getViewState(parentTag); + if (!(parentViewState.mView instanceof ViewGroup)) { + String message = + "Unable to add a view into a view that is not a ViewGroup. ParentTag: " + + parentTag + + " - Tag: " + + tag + + " - Index: " + + index; + FLog.e(TAG, message); + throw new IllegalStateException(message); + } + final ViewGroup parentView = (ViewGroup) parentViewState.mView; + ViewState viewState = getViewState(tag); + final View view = viewState.mView; + if (view == null) { + throw new IllegalStateException( + "Unable to find view for viewState " + viewState + " and tag " + tag); + } + + // Display children before inserting + if (SHOW_CHANGED_VIEW_HIERARCHIES) { + FLog.e(TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE"); + logViewHierarchy(parentView, false); + } + + ViewParent viewParent = view.getParent(); + if (viewParent != null) { + int actualParentId = + viewParent instanceof ViewGroup ? ((ViewGroup) viewParent).getId() : View.NO_ID; + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "addViewAt: cannot insert view [" + + tag + + "] into parent [" + + parentTag + + "]: View already has a parent: [" + + actualParentId + + "] " + + viewParent.getClass().getSimpleName())); + } + + try { + getViewGroupManager(parentViewState).addView(parentView, view, index); + } catch (IllegalStateException e) { + // Wrap error with more context for debugging + throw new IllegalStateException( + "addViewAt: failed to insert view [" + + tag + + "] into parent [" + + parentTag + + "] at index " + + index, + e); + } + + // Display children after inserting + if (SHOW_CHANGED_VIEW_HIERARCHIES) { + // Why are we calling `runOnUiThread`? We're already on the UI thread, right?! + // Yes - but if you get the children of the View here and display them, *it might show you + // the previous children*. Without getting too much into Android internals, basically if we + // wait a tick, everything is what we expect. + // tldr is that `parent.children == []; parent.addView(x); parent.children == []` + // and you need to wait a tick for `parent.children == [x]`. + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + FLog.e( + TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " AFTER"); + logViewHierarchy(parentView, false); + } + }); + } + } + + @UiThread + public void removeViewAt(final int tag, final int parentTag, int index) { + if (isStopped()) { + return; + } + + UiThreadUtil.assertOnUiThread(); + ViewState parentViewState = getNullableViewState(parentTag); + + // TODO: throw exception here? + if (parentViewState == null) { + ReactSoftExceptionLogger.logSoftException( + MountingManager.TAG, + new IllegalStateException( + "Unable to find viewState for tag: [" + parentTag + "] for removeViewAt")); + return; + } + + if (!(parentViewState.mView instanceof ViewGroup)) { + String message = + "Unable to remove a view from a view that is not a ViewGroup. ParentTag: " + + parentTag + + " - Tag: " + + tag + + " - Index: " + + index; + FLog.e(TAG, message); + throw new IllegalStateException(message); + } + + final ViewGroup parentView = (ViewGroup) parentViewState.mView; + + if (parentView == null) { + throw new IllegalStateException("Unable to find view for tag [" + parentTag + "]"); + } + + if (SHOW_CHANGED_VIEW_HIERARCHIES) { + // Display children before deleting any + FLog.e(TAG, "removeViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE"); + logViewHierarchy(parentView, false); + } + + ViewGroupManager viewGroupManager = getViewGroupManager(parentViewState); + + // Verify that the view we're about to remove has the same tag we expect + View view = viewGroupManager.getChildAt(parentView, index); + int actualTag = (view != null ? view.getId() : -1); + if (actualTag != tag) { + int tagActualIndex = -1; + int parentChildrenCount = parentView.getChildCount(); + for (int i = 0; i < parentChildrenCount; i++) { + if (parentView.getChildAt(i).getId() == tag) { + tagActualIndex = i; + break; + } + } + + // TODO T74425739: previously, we did not do this check and `removeViewAt` would be executed + // below, sometimes crashing there. *However*, interestingly enough, `removeViewAt` would not + // complain if you removed views from an already-empty parent. This seems necessary currently + // for certain ViewManagers that remove their own children - like BottomSheet? + // This workaround seems not-great, but for now, we just return here for + // backwards-compatibility. Essentially, if a view has already been removed from the + // hierarchy, we treat it as a noop. + if (tagActualIndex == -1) { + FLog.e( + TAG, + "removeViewAt: [" + + tag + + "] -> [" + + parentTag + + "] @" + + index + + ": view already removed from parent! Children in parent: " + + parentChildrenCount); + return; + } + + // Here we are guaranteed that the view is still in the View hierarchy, just + // at a different index. In debug mode we'll crash here; in production, we'll remove + // the child from the parent and move on. + // This is an issue that is safely recoverable 95% of the time. If this allows corruption + // of the view hierarchy and causes bugs or a crash after this point, there will be logs + // indicating that this happened. + // This is likely *only* necessary because of Fabric's LayoutAnimations implementation. + // If we can fix the bug there, or remove the need for LayoutAnimation index adjustment + // entirely, we can just throw this exception without regression user experience. + logViewHierarchy(parentView, true); + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Tried to remove view [" + + tag + + "] of parent [" + + parentTag + + "] at index " + + index + + ", but got view tag " + + actualTag + + " - actual index of view: " + + tagActualIndex)); + index = tagActualIndex; + } + + try { + viewGroupManager.removeViewAt(parentView, index); + } catch (RuntimeException e) { + // Note: `getChildCount` may not always be accurate! + // We don't currently have a good explanation other than, in situations where you + // would empirically expect to see childCount > 0, the childCount is reported as 0. + // This is likely due to a ViewManager overriding getChildCount or some other methods + // in a way that is strictly incorrect, but potentially only visible here. + // The failure mode is actually that in `removeViewAt`, a NullPointerException is + // thrown when we try to perform an operation on a View that doesn't exist, and + // is therefore null. + // We try to add some extra diagnostics here, but we always try to remove the View + // from the hierarchy first because detecting by looking at childCount will not work. + // + // Note that the lesson here is that `getChildCount` is not /required/ to adhere to + // any invariants. If you add 9 children to a parent, the `getChildCount` of the parent + // may not be equal to 9. This apparently causes no issues with Android and is common + // enough that we shouldn't try to change this invariant, without a lot of thought. + int childCount = viewGroupManager.getChildCount(parentView); + + logViewHierarchy(parentView, true); + + throw new IllegalStateException( + "Cannot remove child at index " + + index + + " from parent ViewGroup [" + + parentView.getId() + + "], only " + + childCount + + " children in parent. Warning: childCount may be incorrect!", + e); + } + + // Display children after deleting any + if (SHOW_CHANGED_VIEW_HIERARCHIES) { + final int finalIndex = index; + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + FLog.e( + TAG, + "removeViewAt: [" + + tag + + "] -> [" + + parentTag + + "] idx: " + + finalIndex + + " AFTER"); + logViewHierarchy(parentView, false); + } + }); + } + } + + @UiThread + public void createView( + @NonNull String componentName, + int reactTag, + @Nullable ReadableMap props, + @Nullable StateWrapper stateWrapper, + @Nullable EventEmitterWrapper eventEmitterWrapper, + boolean isLayoutable) { + if (isStopped()) { + return; + } + // We treat this as a perf problem and not a logical error. View Preallocation or unexpected + // changes to Differ or C++ Binding could cause some redundant Create instructions. + // This is a NoCrash soft exception because we know there are cases where preallocation happens + // and a node is recreated: if a node is preallocated and then committed with revision 2+, + // an extra CREATE instruction will be generated. + // This represents a perf issue only, not a correctness issue. In the future we need to + // refactor View preallocation to correct the currently incorrect assumptions. + if (getNullableViewState(reactTag) != null) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Cannot CREATE view with tag [" + reactTag + "], already exists.")); + return; + } + + createViewUnsafe( + componentName, reactTag, props, stateWrapper, eventEmitterWrapper, isLayoutable); + } + + /** + * Perform view creation without any safety checks. You must ensure safety before calling this + * method (see existing callsites). + * + * @param componentName + * @param reactTag + * @param props + * @param stateWrapper + * @param eventEmitterWrapper + * @param isLayoutable + */ + @UiThread + public void createViewUnsafe( + @NonNull String componentName, + int reactTag, + @Nullable ReadableMap props, + @Nullable StateWrapper stateWrapper, + @Nullable EventEmitterWrapper eventEmitterWrapper, + boolean isLayoutable) { + View view = null; + ViewManager viewManager = null; + + ReactStylesDiffMap propsDiffMap = null; + if (props != null) { + propsDiffMap = new ReactStylesDiffMap(props); + } + + if (isLayoutable) { + viewManager = mViewManagerRegistry.get(componentName); + // View Managers are responsible for dealing with initial state and props. + view = + viewManager.createView( + reactTag, mThemedReactContext, propsDiffMap, stateWrapper, mJSResponderHandler); + } + + ViewState viewState = new ViewState(reactTag, view, viewManager); + viewState.mCurrentProps = propsDiffMap; + viewState.mStateWrapper = stateWrapper; + viewState.mEventEmitter = eventEmitterWrapper; + + mTagToViewState.put(reactTag, viewState); + } + + public void updateProps(int reactTag, ReadableMap props) { + if (isStopped()) { + return; + } + + ViewState viewState = getViewState(reactTag); + viewState.mCurrentProps = new ReactStylesDiffMap(props); + View view = viewState.mView; + + if (view == null) { + throw new IllegalStateException("Unable to find view for tag [" + reactTag + "]"); + } + + Assertions.assertNotNull(viewState.mViewManager) + .updateProperties(view, viewState.mCurrentProps); + } + + @Deprecated + public void receiveCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs) { + if (isStopped()) { + return; + } + + ViewState viewState = getNullableViewState(reactTag); + + // It's not uncommon for JS to send events as/after a component is being removed from the + // view hierarchy. For example, TextInput may send a "blur" command in response to the view + // disappearing. Throw `ReactNoCrashSoftException` so they're logged but don't crash in dev + // for now. + if (viewState == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState for tag: [" + reactTag + "] for commandId: " + commandId); + } + + if (viewState.mViewManager == null) { + throw new RetryableMountingLayerException("Unable to find viewManager for tag " + reactTag); + } + + if (viewState.mView == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState view for tag " + reactTag); + } + + viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs); + } + + public void receiveCommand( + int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) { + if (isStopped()) { + return; + } + + ViewState viewState = getNullableViewState(reactTag); + + // It's not uncommon for JS to send events as/after a component is being removed from the + // view hierarchy. For example, TextInput may send a "blur" command in response to the view + // disappearing. Throw `ReactNoCrashSoftException` so they're logged but don't crash in dev + // for now. + if (viewState == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState for tag: " + reactTag + " for commandId: " + commandId); + } + + if (viewState.mViewManager == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState manager for tag " + reactTag); + } + + if (viewState.mView == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState view for tag " + reactTag); + } + + viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs); + } + + public void sendAccessibilityEvent(int reactTag, int eventType) { + if (isStopped()) { + return; + } + + ViewState viewState = getViewState(reactTag); + + if (viewState.mViewManager == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState manager for tag " + reactTag); + } + + if (viewState.mView == null) { + throw new RetryableMountingLayerException( + "Unable to find viewState view for tag " + reactTag); + } + + viewState.mView.sendAccessibilityEvent(eventType); + } + + @UiThread + public void updateLayout(int reactTag, int x, int y, int width, int height, int displayType) { + if (isStopped()) { + return; + } + + ViewState viewState = getViewState(reactTag); + // Do not layout Root Views + if (viewState.mIsRoot) { + return; + } + + View viewToUpdate = viewState.mView; + if (viewToUpdate == null) { + throw new IllegalStateException("Unable to find View for tag: " + reactTag); + } + + viewToUpdate.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); + + ViewParent parent = viewToUpdate.getParent(); + if (parent instanceof RootView) { + parent.requestLayout(); + } + + // TODO: T31905686 Check if the parent of the view has to layout the view, or the child has + // to lay itself out. see NativeViewHierarchyManager.updateLayout + viewToUpdate.layout(x, y, x + width, y + height); + + // displayType: 0 represents display: 'none' + int visibility = displayType == 0 ? View.INVISIBLE : View.VISIBLE; + if (viewToUpdate.getVisibility() != visibility) { + viewToUpdate.setVisibility(visibility); + } + } + + @UiThread + public void updatePadding(int reactTag, int left, int top, int right, int bottom) { + UiThreadUtil.assertOnUiThread(); + if (isStopped()) { + return; + } + + ViewState viewState = getViewState(reactTag); + // Do not layout Root Views + if (viewState.mIsRoot) { + return; + } + + View viewToUpdate = viewState.mView; + if (viewToUpdate == null) { + throw new IllegalStateException("Unable to find View for tag: " + reactTag); + } + + ViewManager viewManager = viewState.mViewManager; + if (viewManager == null) { + throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); + } + + //noinspection unchecked + viewManager.setPadding(viewToUpdate, left, top, right, bottom); + } + + @UiThread + public void updateState(final int reactTag, @Nullable StateWrapper stateWrapper) { + UiThreadUtil.assertOnUiThread(); + if (isStopped()) { + return; + } + + ViewState viewState = getViewState(reactTag); + + StateWrapper prevStateWrapper = viewState.mStateWrapper; + viewState.mStateWrapper = stateWrapper; + + ViewManager viewManager = viewState.mViewManager; + + if (viewManager == null) { + throw new IllegalStateException("Unable to find ViewManager for tag: " + reactTag); + } + Object extraData = + viewManager.updateState(viewState.mView, viewState.mCurrentProps, stateWrapper); + if (extraData != null) { + viewManager.updateExtraData(viewState.mView, extraData); + } + + // Immediately clear native side of previous state wrapper. This causes the State object in C++ + // to be destroyed immediately instead of waiting for Java GC to kick in. + if (prevStateWrapper != null) { + prevStateWrapper.destroyState(); + } + } + + @UiThread + public void updateEventEmitter(int reactTag, @NonNull EventEmitterWrapper eventEmitter) { + UiThreadUtil.assertOnUiThread(); + if (isStopped()) { + return; + } + + ViewState viewState = mTagToViewState.get(reactTag); + if (viewState == null) { + // TODO T62717437 - Use a flag to determine that these event emitters belong to virtual nodes + // only. + viewState = new ViewState(reactTag, null, null); + mTagToViewState.put(reactTag, viewState); + } + EventEmitterWrapper previousEventEmitterWrapper = viewState.mEventEmitter; + viewState.mEventEmitter = eventEmitter; + + // Immediately destroy native side of wrapper, instead of waiting for Java GC. + if (previousEventEmitterWrapper != eventEmitter && previousEventEmitterWrapper != null) { + previousEventEmitterWrapper.destroy(); + } + } + + @UiThread + public synchronized void setJSResponder( + int reactTag, int initialReactTag, boolean blockNativeResponder) { + UiThreadUtil.assertOnUiThread(); + if (isStopped()) { + return; + } + + if (!blockNativeResponder) { + mJSResponderHandler.setJSResponder(initialReactTag, null); + return; + } + + ViewState viewState = getViewState(reactTag); + View view = viewState.mView; + if (initialReactTag != reactTag && view instanceof ViewParent) { + // In this case, initialReactTag corresponds to a virtual/layout-only View, and we already + // have a parent of that View in reactTag, so we can use it. + mJSResponderHandler.setJSResponder(initialReactTag, (ViewParent) view); + return; + } else if (view == null) { + SoftAssertions.assertUnreachable("Cannot find view for tag [" + reactTag + "]."); + return; + } + + if (viewState.mIsRoot) { + SoftAssertions.assertUnreachable( + "Cannot block native responder on [" + reactTag + "] that is a root view"); + } + mJSResponderHandler.setJSResponder(initialReactTag, view.getParent()); + } + + @UiThread + private void onViewStateDeleted(ViewState viewState) { + // Destroy state immediately instead of waiting for Java GC. + if (viewState.mStateWrapper != null) { + viewState.mStateWrapper.destroyState(); + viewState.mStateWrapper = null; + } + + // Destroy EventEmitterWrapper immediately instead of waiting for Java GC. + // Notably, this is also required to ensure that the EventEmitterWrapper is deallocated + // before the JS VM is deallocated, since it holds onto a JSI::Pointer. + if (viewState.mEventEmitter != null) { + viewState.mEventEmitter.destroy(); + viewState.mEventEmitter = null; + } + + // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} + ViewManager viewManager = viewState.mViewManager; + if (!viewState.mIsRoot && viewManager != null) { + viewManager.onDropViewInstance(viewState.mView); + } + } + + @UiThread + public void deleteView(int reactTag) { + UiThreadUtil.assertOnUiThread(); + if (isStopped()) { + return; + } + + ViewState viewState = getNullableViewState(reactTag); + + if (viewState == null) { + ReactSoftExceptionLogger.logSoftException( + MountingManager.TAG, + new IllegalStateException( + "Unable to find viewState for tag: " + reactTag + " for deleteView")); + return; + } + + // To delete we simply remove the tag from the registry. + // We want to rely on the correct set of MountInstructions being sent to the platform, + // or StopSurface being called, so we do not handle deleting descendents of the View. + mTagToViewState.remove(reactTag); + + onViewStateDeleted(viewState); + } + + @UiThread + public void preallocateView( + String componentName, + int reactTag, + @Nullable ReadableMap props, + @Nullable StateWrapper stateWrapper, + @Nullable EventEmitterWrapper eventEmitterWrapper, + boolean isLayoutable) { + UiThreadUtil.assertOnUiThread(); + + if (isStopped()) { + return; + } + // We treat this as a perf problem and not a logical error. View Preallocation or unexpected + // changes to Differ or C++ Binding could cause some redundant Create instructions. + if (getNullableViewState(reactTag) != null) { + return; + } + + createViewUnsafe( + componentName, reactTag, props, stateWrapper, eventEmitterWrapper, isLayoutable); + } + + @AnyThread + @ThreadConfined(ANY) + public @Nullable EventEmitterWrapper getEventEmitter(int reactTag) { + ViewState viewState = getNullableViewState(reactTag); + return viewState == null ? null : viewState.mEventEmitter; + } + + @UiThread + public View getView(int reactTag) { + ViewState state = getNullableViewState(reactTag); + View view = state == null ? null : state.mView; + if (view == null) { + throw new IllegalViewOperationException( + "Trying to resolve view with tag " + reactTag + " which doesn't exist"); + } + return view; + } + + private @NonNull ViewState getViewState(int tag) { + ViewState viewState = mTagToViewState.get(tag); + if (viewState == null) { + throw new RetryableMountingLayerException("Unable to find viewState for tag " + tag); + } + return viewState; + } + + private @Nullable ViewState getNullableViewState(int tag) { + if (mTagToViewState == null) { + return null; + } + return mTagToViewState.get(tag); + } + + @SuppressWarnings("unchecked") // prevents unchecked conversion warn of the type + private static @NonNull ViewGroupManager getViewGroupManager( + @NonNull ViewState viewState) { + if (viewState.mViewManager == null) { + throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); + } + return (ViewGroupManager) viewState.mViewManager; + } + + public void printSurfaceState() { + FLog.e(TAG, "Views created for surface {%d}:", getSurfaceId()); + for (ViewState viewState : mTagToViewState.values()) { + String viewManagerName = + viewState.mViewManager != null ? viewState.mViewManager.getName() : null; + @Nullable View view = viewState.mView; + @Nullable View parent = view != null ? (View) view.getParent() : null; + @Nullable Integer parentTag = parent != null ? parent.getId() : null; + + FLog.e( + TAG, + "<%s id=%d parentTag=%s isRoot=%b />", + viewManagerName, + viewState.mReactTag, + parentTag, + viewState.mIsRoot); + } + } + + /** + * This class holds view state for react tags. Objects of this class are stored into the {@link + * #mTagToViewState}, and they should be updated in the same thread. + */ + private static class ViewState { + @Nullable final View mView; + final int mReactTag; + final boolean mIsRoot; + @Nullable final ViewManager mViewManager; + @Nullable public ReactStylesDiffMap mCurrentProps = null; + @Nullable public ReadableMap mCurrentLocalData = null; + @Nullable public StateWrapper mStateWrapper = null; + @Nullable public EventEmitterWrapper mEventEmitter = null; + + private ViewState(int reactTag, @Nullable View view, @Nullable ViewManager viewManager) { + this(reactTag, view, viewManager, false); + } + + private ViewState(int reactTag, @Nullable View view, ViewManager viewManager, boolean isRoot) { + mReactTag = reactTag; + mView = view; + mIsRoot = isRoot; + mViewManager = viewManager; + } + + @Override + public String toString() { + boolean isLayoutOnly = mViewManager == null; + return "ViewState [" + + mReactTag + + "] - isRoot: " + + mIsRoot + + " - props: " + + mCurrentProps + + " - localData: " + + mCurrentLocalData + + " - viewManager: " + + mViewManager + + " - isLayoutOnly: " + + isLayoutOnly; + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java deleted file mode 100644 index 9a7e573edd058..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import androidx.annotation.NonNull; -import com.facebook.common.logging.FLog; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.bridge.ReactMarker; -import com.facebook.react.bridge.ReactMarkerConstants; -import com.facebook.react.fabric.mounting.MountingManager; -import com.facebook.systrace.Systrace; - -/** - * This class represents a batch of {@link MountItem}s - * - *

A MountItem batch contains an array of {@link MountItem} and a size. The size determines the - * amount of items that needs to be processed in the array. - * - *

The purpose of encapsulating the array of MountItems this way, is to reduce the amount of - * allocations in C++ - */ -@DoNotStrip -public class BatchMountItem implements MountItem { - static final String TAG = "FabricBatchMountItem"; - - private final int mRootTag; - @NonNull private final MountItem[] mMountItems; - - private final int mSize; - - private final int mCommitNumber; - - public BatchMountItem(int rootTag, MountItem[] items, int size, int commitNumber) { - int itemsLength = (items == null ? 0 : items.length); - if (size < 0 || size > itemsLength) { - throw new IllegalArgumentException( - "Invalid size received by parameter size: " + size + " items.size = " + itemsLength); - } - mRootTag = rootTag; - mMountItems = items; - mSize = size; - mCommitNumber = commitNumber; - } - - private void beginMarkers(String reason) { - Systrace.beginSection( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "FabricUIManager::" + reason + " - " + mSize + " items"); - - if (mCommitNumber > 0) { - ReactMarker.logFabricMarker( - ReactMarkerConstants.FABRIC_BATCH_EXECUTION_START, null, mCommitNumber); - } - } - - private void endMarkers() { - if (mCommitNumber > 0) { - ReactMarker.logFabricMarker( - ReactMarkerConstants.FABRIC_BATCH_EXECUTION_END, null, mCommitNumber); - } - - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - beginMarkers("mountViews"); - - int mountItemIndex = 0; - try { - for (; mountItemIndex < mSize; mountItemIndex++) { - mMountItems[mountItemIndex].execute(mountingManager); - } - } catch (RuntimeException e) { - FLog.e( - TAG, - "Caught exception executing mountItem @" - + mountItemIndex - + ": " - + mMountItems[mountItemIndex].toString(), - e); - throw e; - } - - endMarkers(); - } - - public int getRootTag() { - return mRootTag; - } - - public boolean shouldSchedule() { - return mSize != 0; - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < mSize; i++) { - if (s.length() > 0) { - s.append("\n"); - } - s.append("BatchMountItem [S:" + mRootTag + "] (") - .append(i + 1) - .append("/") - .append(mSize) - .append("): ") - .append(mMountItems[i]); - } - return s.toString(); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java deleted file mode 100644 index d96bab3326e08..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/CreateMountItem.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.fabric.mounting.MountingManager; -import com.facebook.react.uimanager.StateWrapper; -import com.facebook.react.uimanager.ThemedReactContext; - -public class CreateMountItem implements MountItem { - - @NonNull private final String mComponent; - private final int mRootTag; - private final int mReactTag; - @NonNull private final ThemedReactContext mContext; - private final @Nullable ReadableMap mProps; - private final @Nullable StateWrapper mStateWrapper; - private final boolean mIsLayoutable; - - public CreateMountItem( - @Nullable ThemedReactContext context, - int rootTag, - int reactTag, - @NonNull String component, - @Nullable ReadableMap props, - @NonNull StateWrapper stateWrapper, - boolean isLayoutable) { - mContext = context; - mComponent = component; - mRootTag = rootTag; - mReactTag = reactTag; - mProps = props; - mStateWrapper = stateWrapper; - mIsLayoutable = isLayoutable; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - if (mContext == null) { - throw new IllegalStateException( - "Cannot execute PreAllocateViewMountItem without Context for ReactTag: " - + mReactTag - + " and rootTag: " - + mRootTag); - } - mountingManager.createView( - mContext, mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable); - } - - @Override - public String toString() { - return "CreateMountItem [" - + mReactTag - + "] - component: " - + mComponent - + " - rootTag: " - + mRootTag - + " - isLayoutable: " - + mIsLayoutable; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchIntCommandMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchIntCommandMountItem.java index 9d525755bfa38..6f23f9b877998 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchIntCommandMountItem.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchIntCommandMountItem.java @@ -14,20 +14,27 @@ public class DispatchIntCommandMountItem extends DispatchCommandMountItem { + private final int mSurfaceId; private final int mReactTag; private final int mCommandId; private final @Nullable ReadableArray mCommandArgs; public DispatchIntCommandMountItem( - int reactTag, int commandId, @Nullable ReadableArray commandArgs) { + int surfaceId, int reactTag, int commandId, @Nullable ReadableArray commandArgs) { + mSurfaceId = surfaceId; mReactTag = reactTag; mCommandId = commandId; mCommandArgs = commandArgs; } + @Override + public int getSurfaceId() { + return mSurfaceId; + } + @Override public void execute(@NonNull MountingManager mountingManager) { - mountingManager.receiveCommand(mReactTag, mCommandId, mCommandArgs); + mountingManager.receiveCommand(mSurfaceId, mReactTag, mCommandId, mCommandArgs); } @Override diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchStringCommandMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchStringCommandMountItem.java index 0b04e25f161d0..1d76091b6ae03 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchStringCommandMountItem.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/DispatchStringCommandMountItem.java @@ -14,20 +14,27 @@ public class DispatchStringCommandMountItem extends DispatchCommandMountItem { + private final int mSurfaceId; private final int mReactTag; @NonNull private final String mCommandId; private final @Nullable ReadableArray mCommandArgs; public DispatchStringCommandMountItem( - int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) { + int surfaceId, int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) { + mSurfaceId = surfaceId; mReactTag = reactTag; mCommandId = commandId; mCommandArgs = commandArgs; } + @Override + public int getSurfaceId() { + return mSurfaceId; + } + @Override public void execute(@NonNull MountingManager mountingManager) { - mountingManager.receiveCommand(mReactTag, mCommandId, mCommandArgs); + mountingManager.receiveCommand(mSurfaceId, mReactTag, mCommandId, mCommandArgs); } @Override diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/InsertMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/InsertMountItem.java deleted file mode 100644 index edfc46ac7109c..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/InsertMountItem.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import androidx.annotation.NonNull; -import com.facebook.react.fabric.mounting.MountingManager; - -public class InsertMountItem implements MountItem { - - private int mReactTag; - private int mParentReactTag; - private int mIndex; - - public InsertMountItem(int reactTag, int parentReactTag, int index) { - mReactTag = reactTag; - mParentReactTag = parentReactTag; - mIndex = index; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.addViewAt(mParentReactTag, mReactTag, mIndex); - } - - public int getParentReactTag() { - return mParentReactTag; - } - - public int getIndex() { - return mIndex; - } - - @Override - public String toString() { - return "InsertMountItem [" - + mReactTag - + "] - parentTag: [" - + mParentReactTag - + "] - index: " - + mIndex; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java index efaafa11ead5a..038596c6ac493 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.java @@ -8,10 +8,10 @@ package com.facebook.react.fabric.mounting.mountitems; import static com.facebook.react.fabric.FabricComponents.getFabricComponentName; +import static com.facebook.react.fabric.FabricUIManager.ENABLE_FABRIC_LOGS; import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactMarker; @@ -19,8 +19,8 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.mounting.MountingManager; +import com.facebook.react.fabric.mounting.SurfaceMountingManager; import com.facebook.react.uimanager.StateWrapper; -import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.systrace.Systrace; /** @@ -50,26 +50,18 @@ public class IntBufferBatchMountItem implements MountItem { static final int INSTRUCTION_UPDATE_EVENT_EMITTER = 256; static final int INSTRUCTION_UPDATE_PADDING = 512; - private final int mRootTag; + private final int mSurfaceId; private final int mCommitNumber; - @NonNull private final ThemedReactContext mContext; - @NonNull private final int[] mIntBuffer; @NonNull private final Object[] mObjBuffer; private final int mIntBufferLen; private final int mObjBufferLen; - public IntBufferBatchMountItem( - int rootTag, - @Nullable ThemedReactContext context, - int[] intBuf, - Object[] objBuf, - int commitNumber) { - mRootTag = rootTag; + public IntBufferBatchMountItem(int surfaceId, int[] intBuf, Object[] objBuf, int commitNumber) { + mSurfaceId = surfaceId; mCommitNumber = commitNumber; - mContext = context; mIntBuffer = intBuf; mObjBuffer = objBuf; @@ -119,13 +111,21 @@ private static EventEmitterWrapper castToEventEmitter(Object obj) { @Override public void execute(@NonNull MountingManager mountingManager) { - if (mContext == null) { + SurfaceMountingManager surfaceMountingManager = mountingManager.getSurfaceManager(mSurfaceId); + if (surfaceMountingManager == null) { FLog.e( TAG, - "Cannot execute batch of %s MountItems; no context. Hopefully this is because StopSurface was called.", - TAG); + "Skipping batch of MountItems; no SurfaceMountingManager found for [%d].", + mSurfaceId); + return; + } + if (surfaceMountingManager.isStopped()) { + FLog.e(TAG, "Skipping batch of MountItems; was stopped [%d].", mSurfaceId); return; } + if (ENABLE_FABRIC_LOGS) { + FLog.d(TAG, "Executing IntBufferBatchMountItem on surface [%d]", mSurfaceId); + } beginMarkers("mountViews"); @@ -137,36 +137,40 @@ public void execute(@NonNull MountingManager mountingManager) { for (int k = 0; k < numInstructions; k++) { if (type == INSTRUCTION_CREATE) { String componentName = getFabricComponentName((String) mObjBuffer[j++]); - mountingManager.createView( - mContext, + surfaceMountingManager.createView( componentName, mIntBuffer[i++], castToProps(mObjBuffer[j++]), castToState(mObjBuffer[j++]), + castToEventEmitter(mObjBuffer[j++]), mIntBuffer[i++] == 1); } else if (type == INSTRUCTION_DELETE) { - mountingManager.deleteView(mIntBuffer[i++]); + surfaceMountingManager.deleteView(mIntBuffer[i++]); } else if (type == INSTRUCTION_INSERT) { int tag = mIntBuffer[i++]; int parentTag = mIntBuffer[i++]; - mountingManager.addViewAt(parentTag, tag, mIntBuffer[i++]); + surfaceMountingManager.addViewAt(parentTag, tag, mIntBuffer[i++]); } else if (type == INSTRUCTION_REMOVE) { - mountingManager.removeViewAt(mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]); + surfaceMountingManager.removeViewAt(mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]); } else if (type == INSTRUCTION_UPDATE_PROPS) { - mountingManager.updateProps(mIntBuffer[i++], castToProps(mObjBuffer[j++])); + surfaceMountingManager.updateProps(mIntBuffer[i++], castToProps(mObjBuffer[j++])); } else if (type == INSTRUCTION_UPDATE_STATE) { - mountingManager.updateState(mIntBuffer[i++], castToState(mObjBuffer[j++])); + surfaceMountingManager.updateState(mIntBuffer[i++], castToState(mObjBuffer[j++])); } else if (type == INSTRUCTION_UPDATE_LAYOUT) { - mountingManager.updateLayout( - mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]); + int reactTag = mIntBuffer[i++]; + int x = mIntBuffer[i++]; + int y = mIntBuffer[i++]; + int width = mIntBuffer[i++]; + int height = mIntBuffer[i++]; + int displayType = mIntBuffer[i++]; + surfaceMountingManager.updateLayout(reactTag, x, y, width, height, displayType); - // The final buffer, layoutDirection, seems unused? - i++; } else if (type == INSTRUCTION_UPDATE_PADDING) { - mountingManager.updatePadding( + surfaceMountingManager.updatePadding( mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]); } else if (type == INSTRUCTION_UPDATE_EVENT_EMITTER) { - mountingManager.updateEventEmitter(mIntBuffer[i++], castToEventEmitter(mObjBuffer[j++])); + surfaceMountingManager.updateEventEmitter( + mIntBuffer[i++], castToEventEmitter(mObjBuffer[j++])); } else { throw new IllegalArgumentException( "Invalid type argument to IntBufferBatchMountItem: " + type + " at index: " + i); @@ -177,8 +181,9 @@ public void execute(@NonNull MountingManager mountingManager) { endMarkers(); } - public int getRootTag() { - return mRootTag; + @Override + public int getSurfaceId() { + return mSurfaceId; } public boolean shouldSchedule() { @@ -189,7 +194,7 @@ public boolean shouldSchedule() { public String toString() { try { StringBuilder s = new StringBuilder(); - s.append("IntBufferBatchMountItem:"); + s.append(String.format("IntBufferBatchMountItem [surface:%d]:\n", mSurfaceId)); int i = 0, j = 0; while (i < mIntBufferLen) { int rawType = mIntBuffer[i++]; @@ -198,7 +203,7 @@ public String toString() { for (int k = 0; k < numInstructions; k++) { if (type == INSTRUCTION_CREATE) { String componentName = getFabricComponentName((String) mObjBuffer[j++]); - j += 2; + j += 3; s.append( String.format( "CREATE [%d] - layoutable:%d - %s\n", @@ -221,12 +226,16 @@ public String toString() { : ""; s.append(String.format("UPDATE PROPS [%d]: %s\n", mIntBuffer[i++], propsString)); } else if (type == INSTRUCTION_UPDATE_STATE) { - j += 1; - s.append(String.format("UPDATE STATE [%d]\n", mIntBuffer[i++])); + StateWrapper state = castToState(mObjBuffer[j++]); + String stateString = + IS_DEVELOPMENT_ENVIRONMENT + ? (state != null ? state.getStateData().toString() : "") + : ""; + s.append(String.format("UPDATE STATE [%d]: %s\n", mIntBuffer[i++], stateString)); } else if (type == INSTRUCTION_UPDATE_LAYOUT) { s.append( String.format( - "UPDATE LAYOUT [%d]: x:%d y:%d w:%d h:%d layoutDirection:%d\n", + "UPDATE LAYOUT [%d]: x:%d y:%d w:%d h:%d displayType:%d\n", mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/MountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/MountItem.java index dc053625c2a6d..5ec3aeea571a4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/MountItem.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/MountItem.java @@ -7,6 +7,7 @@ package com.facebook.react.fabric.mounting.mountitems; +import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.UiThread; import com.facebook.react.fabric.mounting.MountingManager; @@ -16,4 +17,7 @@ public interface MountItem { /** Execute this {@link MountItem} into the operation queue received by parameter. */ @UiThread void execute(@NonNull MountingManager mountingManager); + + @AnyThread + int getSurfaceId(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java index 0e3c7140eafbf..5a0f5241bc8f0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PreAllocateViewMountItem.java @@ -7,7 +7,6 @@ package com.facebook.react.fabric.mounting.mountitems; -import static com.facebook.react.fabric.FabricUIManager.ENABLE_FABRIC_LOGS; import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT; import static com.facebook.react.fabric.FabricUIManager.TAG; @@ -15,56 +14,55 @@ import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.mounting.MountingManager; +import com.facebook.react.fabric.mounting.SurfaceMountingManager; import com.facebook.react.uimanager.StateWrapper; -import com.facebook.react.uimanager.ThemedReactContext; /** {@link MountItem} that is used to pre-allocate views for JS components. */ public class PreAllocateViewMountItem implements MountItem { @NonNull private final String mComponent; - private final int mRootTag; + private final int mSurfaceId; private final int mReactTag; private final @Nullable ReadableMap mProps; private final @Nullable StateWrapper mStateWrapper; - private final @NonNull ThemedReactContext mContext; + private final @Nullable EventEmitterWrapper mEventEmitterWrapper; private final boolean mIsLayoutable; public PreAllocateViewMountItem( - @Nullable ThemedReactContext context, - int rootTag, + int surfaceId, int reactTag, @NonNull String component, @Nullable ReadableMap props, @NonNull StateWrapper stateWrapper, + @Nullable EventEmitterWrapper eventEmitterWrapper, boolean isLayoutable) { - mContext = context; mComponent = component; - mRootTag = rootTag; + mSurfaceId = surfaceId; mProps = props; mStateWrapper = stateWrapper; + mEventEmitterWrapper = eventEmitterWrapper; mReactTag = reactTag; mIsLayoutable = isLayoutable; } - public int getRootTag() { - return mRootTag; + @Override + public int getSurfaceId() { + return mSurfaceId; } @Override public void execute(@NonNull MountingManager mountingManager) { - if (ENABLE_FABRIC_LOGS) { - FLog.d(TAG, "Executing pre-allocation of: " + toString()); - } - if (mContext == null) { - throw new IllegalStateException( - "Cannot execute PreAllocateViewMountItem without Context for ReactTag: " - + mReactTag - + " and rootTag: " - + mRootTag); + SurfaceMountingManager surfaceMountingManager = mountingManager.getSurfaceManager(mSurfaceId); + if (surfaceMountingManager == null) { + FLog.e( + TAG, + "Skipping View PreAllocation; no SurfaceMountingManager found for [" + mSurfaceId + "]"); + return; } - mountingManager.preallocateView( - mContext, mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable); + surfaceMountingManager.preallocateView( + mComponent, mReactTag, mProps, mStateWrapper, mEventEmitterWrapper, mIsLayoutable); } @Override @@ -74,8 +72,8 @@ public String toString() { .append(mReactTag) .append("] - component: ") .append(mComponent) - .append(" rootTag: ") - .append(mRootTag) + .append(" surfaceId: ") + .append(mSurfaceId) .append(" isLayoutable: ") .append(mIsLayoutable); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/RemoveDeleteMultiMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/RemoveDeleteMultiMountItem.java deleted file mode 100644 index daf3ed5ca75d5..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/RemoveDeleteMultiMountItem.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import androidx.annotation.NonNull; -import com.facebook.react.fabric.mounting.MountingManager; - -public class RemoveDeleteMultiMountItem implements MountItem { - - // Metadata is an array of ints, grouped into 4 ints per instruction (so the length of metadata - // is always divisible by 4): - // - // `instruction*4 + 0`: react tag of view instruction - // `instruction*4 + 1`: react tag of view's parent - // `instruction*4 + 2`: index of view in parents' children instruction - // `instruction*4 + 3`: flags indicating if the view should be removed, and/or deleted - @NonNull private int[] mMetadata; - - // Bitfields of "flag", indicating if a view should be removed and/or deleted - private static final int REMOVE_FLAG = 1; - private static final int DELETE_FLAG = 2; - - // Indices for each parameter within an "instruction" - private static final int INSTRUCTION_FIELDS_LEN = 4; - private static final int TAG_INDEX = 0; - private static final int PARENT_TAG_INDEX = 1; - private static final int VIEW_INDEX_INDEX = 2; - private static final int FLAGS_INDEX = 3; - - public RemoveDeleteMultiMountItem(@NonNull int[] metadata) { - mMetadata = metadata; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - // First, go through instructions and remove all views that are marked - // for removal. - // Not all views that are removed are deleted, and not all deleted views - // are removed first. - // *All* views must be removed here before we can delete any views. - // Removal of a view from a parent is based on indices within the parents' children, - // and deletion causes reordering; so we must perform all removals first. - for (int i = 0; i < mMetadata.length; i += INSTRUCTION_FIELDS_LEN) { - int flags = mMetadata[i + FLAGS_INDEX]; - if ((flags & REMOVE_FLAG) != 0) { - int parentTag = mMetadata[i + PARENT_TAG_INDEX]; - int tag = mMetadata[i + TAG_INDEX]; - int index = mMetadata[i + VIEW_INDEX_INDEX]; - mountingManager.removeViewAt(tag, parentTag, index); - } - } - - // After removing all views, delete all views marked for deletion. - for (int i = 0; i < mMetadata.length; i += 4) { - int flags = mMetadata[i + FLAGS_INDEX]; - if ((flags & DELETE_FLAG) != 0) { - int tag = mMetadata[i + TAG_INDEX]; - mountingManager.deleteView(tag); - } - } - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < mMetadata.length; i += 4) { - if (s.length() > 0) { - s.append("\n"); - } - s.append("RemoveDeleteMultiMountItem (") - .append(i / INSTRUCTION_FIELDS_LEN + 1) - .append("/") - .append(mMetadata.length / INSTRUCTION_FIELDS_LEN) - .append("): [") - .append(mMetadata[i + TAG_INDEX]) - .append("] parent [") - .append(mMetadata[i + PARENT_TAG_INDEX]) - .append("] idx ") - .append(mMetadata[i + VIEW_INDEX_INDEX]) - .append(" ") - .append(mMetadata[i + FLAGS_INDEX]); - } - return s.toString(); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java index 5852ff1a78c64..dc69db7a04ab1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java @@ -8,7 +8,7 @@ package com.facebook.react.fabric.mounting.mountitems; import androidx.annotation.NonNull; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.RetryableMountingLayerException; import com.facebook.react.fabric.mounting.MountingManager; @@ -16,10 +16,12 @@ public class SendAccessibilityEvent implements MountItem { private final String TAG = "Fabric.SendAccessibilityEvent"; + private final int mSurfaceId; private final int mReactTag; private final int mEventType; - public SendAccessibilityEvent(int reactTag, int eventType) { + public SendAccessibilityEvent(int surfaceId, int reactTag, int eventType) { + mSurfaceId = surfaceId; mReactTag = reactTag; mEventType = eventType; } @@ -27,7 +29,7 @@ public SendAccessibilityEvent(int reactTag, int eventType) { @Override public void execute(@NonNull MountingManager mountingManager) { try { - mountingManager.sendAccessibilityEvent(mReactTag, mEventType); + mountingManager.sendAccessibilityEvent(mSurfaceId, mReactTag, mEventType); } catch (RetryableMountingLayerException e) { // Accessibility events are similar to commands in that they're imperative // calls from JS, disconnected from the commit lifecycle, and therefore @@ -36,10 +38,15 @@ public void execute(@NonNull MountingManager mountingManager) { // due to race conditions (like the view disappearing after the event is // queued and before it executes), we log a soft exception and continue along. // Other categories of errors will still cause a hard crash. - ReactSoftException.logSoftException(TAG, e); + ReactSoftExceptionLogger.logSoftException(TAG, e); } } + @Override + public int getSurfaceId() { + return mSurfaceId; + } + @Override public String toString() { return "SendAccessibilityEvent [" + mReactTag + "] " + mEventType; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateEventEmitterMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateEventEmitterMountItem.java deleted file mode 100644 index 72331c6b96348..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateEventEmitterMountItem.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import androidx.annotation.NonNull; -import com.facebook.react.fabric.events.EventEmitterWrapper; -import com.facebook.react.fabric.mounting.MountingManager; - -public class UpdateEventEmitterMountItem implements MountItem { - - @NonNull private final EventEmitterWrapper mEventHandler; - private final int mReactTag; - - public UpdateEventEmitterMountItem(int reactTag, @NonNull EventEmitterWrapper EventHandler) { - mReactTag = reactTag; - mEventHandler = EventHandler; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.updateEventEmitter(mReactTag, mEventHandler); - } - - @Override - public String toString() { - return "UpdateEventEmitterMountItem [" + mReactTag + "]"; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLayoutMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLayoutMountItem.java deleted file mode 100644 index cd8edd887e205..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLayoutMountItem.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import android.annotation.TargetApi; -import android.os.Build; -import android.util.LayoutDirection; -import androidx.annotation.NonNull; -import com.facebook.react.fabric.mounting.MountingManager; - -public class UpdateLayoutMountItem implements MountItem { - - private final int mReactTag; - private final int mX; - private final int mY; - private final int mWidth; - private final int mHeight; - private final int mLayoutDirection; - - public UpdateLayoutMountItem( - int reactTag, int x, int y, int width, int height, int layoutDirection) { - mReactTag = reactTag; - mX = x; - mY = y; - mWidth = width; - mHeight = height; - mLayoutDirection = convertLayoutDirection(layoutDirection); - } - - // TODO move this from here - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private static int convertLayoutDirection(int layoutDirection) { - switch (layoutDirection) { - case 0: - return LayoutDirection.INHERIT; - case 1: - return LayoutDirection.LTR; - case 2: - return LayoutDirection.RTL; - default: - throw new IllegalArgumentException("Unsupported layout direction: " + layoutDirection); - } - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.updateLayout(mReactTag, mX, mY, mWidth, mHeight); - } - - public int getX() { - return mX; - } - - public int getY() { - return mY; - } - - public int getHeight() { - return mHeight; - } - - public int getWidth() { - return mWidth; - } - - public int getLayoutDirection() { - return mLayoutDirection; - } - - @Override - public String toString() { - return "UpdateLayoutMountItem [" - + mReactTag - + "] - x: " - + mX - + " - y: " - + mY - + " - height: " - + mHeight - + " - width: " - + mWidth - + " - layoutDirection: " - + +mLayoutDirection; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePaddingMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePaddingMountItem.java deleted file mode 100644 index e0403627bbd99..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePaddingMountItem.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import androidx.annotation.NonNull; -import com.facebook.react.fabric.mounting.MountingManager; - -/** - * A MountItem that represents setting the padding properties of a view (left, top, right, bottom). - * This is distinct from layout because it happens far less frequently from layout; so it is a perf - * optimization to transfer this data and execute the methods strictly less than the layout-related - * properties. - */ -public class UpdatePaddingMountItem implements MountItem { - - private final int mReactTag; - private final int mLeft; - private final int mTop; - private final int mRight; - private final int mBottom; - - public UpdatePaddingMountItem(int reactTag, int left, int top, int right, int bottom) { - mReactTag = reactTag; - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.updatePadding(mReactTag, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "UpdatePaddingMountItem [" - + mReactTag - + "] - left: " - + mLeft - + " - top: " - + mTop - + " - right: " - + mRight - + " - bottom: " - + mBottom; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePropsMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePropsMountItem.java deleted file mode 100644 index 8b980705e9dc8..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdatePropsMountItem.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT; - -import androidx.annotation.NonNull; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.fabric.mounting.MountingManager; - -public class UpdatePropsMountItem implements MountItem { - - private final int mReactTag; - @NonNull private final ReadableMap mUpdatedProps; - - public UpdatePropsMountItem(int reactTag, @NonNull ReadableMap updatedProps) { - mReactTag = reactTag; - mUpdatedProps = updatedProps; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.updateProps(mReactTag, mUpdatedProps); - } - - @Override - public String toString() { - StringBuilder result = - new StringBuilder("UpdatePropsMountItem [").append(mReactTag).append("]"); - - if (IS_DEVELOPMENT_ENVIRONMENT) { - result.append(" props: ").append(mUpdatedProps); - } - - return result.toString(); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateStateMountItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateStateMountItem.java deleted file mode 100644 index 4544e8f935e23..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateStateMountItem.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.fabric.mounting.mountitems; - -import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.facebook.react.fabric.mounting.MountingManager; -import com.facebook.react.uimanager.StateWrapper; - -public class UpdateStateMountItem implements MountItem { - - private final int mReactTag; - @Nullable private final StateWrapper mStateWrapper; - - public UpdateStateMountItem(int reactTag, @Nullable StateWrapper stateWrapper) { - mReactTag = reactTag; - mStateWrapper = stateWrapper; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.updateState(mReactTag, mStateWrapper); - } - - @Override - public String toString() { - StringBuilder result = - new StringBuilder("UpdateStateMountItem [").append(mReactTag).append("]"); - - if (IS_DEVELOPMENT_ENVIRONMENT) { - result.append(" state: ").append(mStateWrapper != null ? mStateWrapper.getState() : ""); - } - - return result.toString(); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/Android.mk index c521147677c16..19a28eee5af25 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/Android.mk @@ -15,7 +15,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS += -fvisibility=hidden -fexceptions -frtti -LOCAL_STATIC_LIBRARIES := libjsi libjsireact jscruntime -LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni +LOCAL_STATIC_LIBRARIES := libjsireact jscruntime +LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libjsi include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK index a1a374ef936b3..54036fce0cf2b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_nat rn_android_library( name = "jscexecutor", srcs = glob(["*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -19,7 +20,8 @@ rn_xplat_cxx_library( srcs = glob(["*.cpp"]), headers = glob(["*.h"]), header_namespace = "", - compiler_flags = ["-fexceptions"], + compiler_flags_enable_exceptions = True, # TODO: is this necessary? + compiler_flags_enable_rtti = True, # TODO: is this necessary? fbandroid_allow_jni_merging = True, platforms = ANDROID, soname = "libjscexecutor.$(ext)", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java b/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java index 09737c178b985..4adb377de8136 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/JSCExecutor.java @@ -14,8 +14,13 @@ import com.facebook.soloader.SoLoader; @DoNotStrip -/* package */ class JSCExecutor extends JavaScriptExecutor { +/* package */ public class JSCExecutor extends JavaScriptExecutor { + static { + loadLibrary(); + } + + public static void loadLibrary() throws UnsatisfiedLinkError { SoLoader.loadLibrary("jscexecutor"); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK index 26444912bf4e7..d5a734f79243e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "jstasks", srcs = glob(["*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/HeadlessJsTaskContext.java b/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/HeadlessJsTaskContext.java index b6f60da4e1778..d31c1371c3c8f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/HeadlessJsTaskContext.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/jstasks/HeadlessJsTaskContext.java @@ -11,7 +11,7 @@ import android.util.SparseArray; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.LifecycleState; import com.facebook.react.modules.appregistry.AppRegistry; @@ -107,12 +107,12 @@ private synchronized void startTask(final HeadlessJsTaskConfig taskConfig, int t } mActiveTasks.add(taskId); mActiveTaskConfigs.put(taskId, new HeadlessJsTaskConfig(taskConfig)); - if (reactContext.hasActiveCatalystInstance()) { + if (reactContext.hasActiveReactInstance()) { reactContext .getJSModule(AppRegistry.class) .startHeadlessTask(taskId, taskConfig.getTaskKey(), taskConfig.getData()); } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( "HeadlessJsTaskContext", new RuntimeException("Cannot start headless task, CatalystInstance not available")); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK index b213c9d3133a0..ec9fc4e9e07e9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "annotations", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK index 85f23a35691b6..22d2f5a662589 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "model", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java index 85a2ac54f2eec..ad148228cd9bc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java @@ -56,7 +56,7 @@ public void onChange(boolean selfChange) { @Override public void onChange(boolean selfChange, Uri uri) { - if (getReactApplicationContext().hasActiveCatalystInstance()) { + if (getReactApplicationContext().hasActiveReactInstance()) { AccessibilityInfoModule.this.updateAndSendReduceMotionChangeEvent(); } } @@ -67,6 +67,7 @@ public void onChange(boolean selfChange, Uri uri) { private final ContentResolver mContentResolver; private boolean mReduceMotionEnabled = false; private boolean mTouchExplorationEnabled = false; + private int mRecommendedTimeout; private static final String REDUCE_MOTION_EVENT_NAME = "reduceMotionDidChange"; private static final String TOUCH_EXPLORATION_EVENT_NAME = "touchExplorationDidChange"; @@ -163,9 +164,13 @@ public void initialize() { } @Override - public void onCatalystInstanceDestroy() { - super.onCatalystInstanceDestroy(); - getReactApplicationContext().removeLifecycleEventListener(this); + public void invalidate() { + super.invalidate(); + + ReactApplicationContext applicationContext = getReactApplicationContextIfActiveOrWarn(); + if (applicationContext != null) { + applicationContext.removeLifecycleEventListener(this); + } } @Override @@ -189,4 +194,16 @@ public void announceForAccessibility(String message) { public void setAccessibilityFocus(double reactTag) { // iOS only } + + @Override + public void getRecommendedTimeoutMillis(double originalTimeout, Callback successCallback) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + successCallback.invoke((int) originalTimeout); + return; + } + mRecommendedTimeout = + mAccessibilityManager.getRecommendedTimeoutMillis( + (int) originalTimeout, AccessibilityManager.FLAG_CONTENT_CONTROLS); + successCallback.invoke(mRecommendedTimeout); + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK index 832ee9de35595..7c6b352a8c1dd 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "accessibilityinfo", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -18,5 +19,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java index 361b3c2795ccb..a5c3db17991b4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.java @@ -7,6 +7,7 @@ package com.facebook.react.modules.appearance; +import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import androidx.annotation.Nullable; @@ -90,7 +91,15 @@ public String getName() { @Override public String getColorScheme() { - mColorScheme = colorSchemeForCurrentConfiguration(getReactApplicationContext()); + // Attempt to use the Activity context first in order to get the most up to date + // scheme. This covers the scenario when AppCompatDelegate.setDefaultNightMode() + // is called directly (which can occur in Brownfield apps for example). + Activity activity = getCurrentActivity(); + + mColorScheme = + colorSchemeForCurrentConfiguration( + activity != null ? activity : getReactApplicationContext()); + return mColorScheme; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK index ed7eba28bfb90..ac20f7869a3db 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "appearance", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -16,5 +17,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK index d54b2cd82034d..727a8d30d1ee9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_target", "rn_android_li rn_android_library( name = "appregistry", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java index 396e525d5f915..4b5c08a4c31e6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java @@ -100,7 +100,7 @@ private void sendEvent(String eventName, @Nullable Object data) { // We don't gain anything interesting from logging here, and it's an extremely common // race condition for an AppState event to be triggered as the Catalyst instance is being // set up or torn down. So, just fail silently here. - if (!reactApplicationContext.hasActiveCatalystInstance()) { + if (!reactApplicationContext.hasActiveReactInstance()) { return; } reactApplicationContext.getJSModule(RCTDeviceEventEmitter.class).emit(eventName, data); @@ -119,4 +119,14 @@ public void addListener(String eventName) { public void removeListeners(double count) { // iOS only } + + @Override + public void invalidate() { + super.invalidate(); + + ReactApplicationContext applicationContext = getReactApplicationContextIfActiveOrWarn(); + if (applicationContext != null) { + applicationContext.removeLifecycleEventListener(this); + } + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK index f1b8ad181290c..d9f4a85e622fa 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "appstate", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -16,5 +17,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK index 266f87cf28f7b..8a675604d8e31 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "blob", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -31,6 +32,6 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/websocket:websocket"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobProvider.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobProvider.java index baf039a223128..96141bafb6a08 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobProvider.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobProvider.java @@ -20,9 +20,15 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public final class BlobProvider extends ContentProvider { + private static final int PIPE_CAPACITY = 65536; + + private ExecutorService executor = Executors.newSingleThreadExecutor(); + @Override public boolean onCreate() { return true; @@ -72,7 +78,7 @@ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundEx throw new RuntimeException("No blob module associated with BlobProvider"); } - byte[] data = blobModule.resolve(uri); + final byte[] data = blobModule.resolve(uri); if (data == null) { throw new FileNotFoundException("Cannot open " + uri.toString() + ", blob not found."); } @@ -84,12 +90,34 @@ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundEx return null; } ParcelFileDescriptor readSide = pipe[0]; - ParcelFileDescriptor writeSide = pipe[1]; - - try (OutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) { - outputStream.write(data); - } catch (IOException exception) { - return null; + final ParcelFileDescriptor writeSide = pipe[1]; + + if (data.length <= PIPE_CAPACITY) { + // If the blob length is less than or equal to pipe capacity (64 KB), + // we can write the data synchronously to the pipe buffer. + try (OutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) { + outputStream.write(data); + } catch (IOException exception) { + return null; + } + } else { + // For blobs larger than 64 KB, a synchronous write would fill up the whole buffer + // and block forever, because there are no readers to empty the buffer. + // Writing from a separate thread allows us to return the read side descriptor + // immediately so that both writer and reader can work concurrently. + // Reading from the pipe empties the buffer and allows the next chunks to be written. + Runnable writer = + new Runnable() { + public void run() { + try (OutputStream outputStream = + new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) { + outputStream.write(data); + } catch (IOException exception) { + // no-op + } + } + }; + executor.submit(writer); } return readSide; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/Android.mk index 53fb0c2bf557e..6c5a7224c78a9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/Android.mk @@ -15,7 +15,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS += -fvisibility=hidden -fexceptions -frtti -LOCAL_STATIC_LIBRARIES := libjsi libjsireact -LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni +LOCAL_STATIC_LIBRARIES := libjsireact +LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libjsi include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK index 01a57b64d3e32..e706c5bba91c0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK @@ -5,12 +5,6 @@ rn_xplat_cxx_library( srcs = glob(["*.cpp"]), headers = glob(["*.h"]), header_namespace = "", - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, soname = "libreactnativeblob.$(ext)", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BlobCollector.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BlobCollector.cpp index 8883a318d8962..285e881472aa0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BlobCollector.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BlobCollector.cpp @@ -29,6 +29,7 @@ BlobCollector::~BlobCollector() { static auto removeMethod = jni::findClassStatic(kBlobModuleJavaDescriptor) ->getMethod("remove"); removeMethod(blobModule_, jni::make_jstring(blobId_).get()); + blobModule_.reset(); }); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/bundleloader/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/bundleloader/BUCK index dcc9cbd7a419b..a6957bf014ff3 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/bundleloader/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/bundleloader/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "bundleloader", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -17,5 +18,5 @@ rn_android_library( react_native_target("java/com/facebook/react/devsupport:interfaces"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK index 217b26ea0deb3..c75487f88425a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "camera", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -18,6 +19,6 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK index d755fa2dd9aa5..0f9a670a69be0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "clipboard", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -15,4 +16,7 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], + exported_deps = [ + react_native_root_target(":FBReactNativeSpec"), + ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/ClipboardModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/ClipboardModule.java index 43f8b5c69dab4..fd152947efd46 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/ClipboardModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/ClipboardModule.java @@ -9,17 +9,16 @@ import android.content.ClipData; import android.content.ClipboardManager; -import android.content.Context; -import com.facebook.react.bridge.ContextBaseJavaModule; +import com.facebook.fbreact.specs.NativeClipboardSpec; import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.module.annotations.ReactModule; /** A module that allows JS to get/set clipboard contents. */ @ReactModule(name = ClipboardModule.NAME) -public class ClipboardModule extends ContextBaseJavaModule { +public class ClipboardModule extends NativeClipboardSpec { - public ClipboardModule(Context context) { + public ClipboardModule(ReactApplicationContext context) { super(context); } @@ -31,10 +30,12 @@ public String getName() { } private ClipboardManager getClipboardService() { - return (ClipboardManager) getContext().getSystemService(getContext().CLIPBOARD_SERVICE); + return (ClipboardManager) + getReactApplicationContext() + .getSystemService(getReactApplicationContext().CLIPBOARD_SERVICE); } - @ReactMethod + @Override public void getString(Promise promise) { try { ClipboardManager clipboard = getClipboardService(); @@ -50,7 +51,7 @@ public void getString(Promise promise) { } } - @ReactMethod + @Override public void setString(String text) { ClipData clipdata = ClipData.newPlainText(null, text); ClipboardManager clipboard = getClipboardService(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK index 47dad24dd0491..d42deef1d60f4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "common", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK index c8694abe9810e..1cd3c671ef784 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "core", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -21,6 +22,6 @@ rn_android_library( react_native_target("java/com/facebook/react/util:util"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.java index 3551447ad111b..c77fb98d01fa9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.java @@ -86,25 +86,14 @@ public void reportSoftException(String message, ReadableArray stack, double idDo public void reportException(ReadableMap data) { String message = data.hasKey("message") ? data.getString("message") : ""; ReadableArray stack = data.hasKey("stack") ? data.getArray("stack") : Arguments.createArray(); - int id = data.hasKey("id") ? data.getInt("id") : -1; boolean isFatal = data.hasKey("isFatal") ? data.getBoolean("isFatal") : false; - if (mDevSupportManager.getDevSupportEnabled()) { - boolean suppressRedBox = false; - if (data.getMap("extraData") != null && data.getMap("extraData").hasKey("suppressRedBox")) { - suppressRedBox = data.getMap("extraData").getBoolean("suppressRedBox"); - } - if (!suppressRedBox) { - mDevSupportManager.showNewJSError(message, stack, id); - } + String extraDataAsJson = ExceptionDataHelper.getExtraDataAsJson(data); + if (isFatal) { + throw new JavascriptException(JSStackTrace.format(message, stack)).setExtraDataAsJson(extraDataAsJson); } else { - String extraDataAsJson = ExceptionDataHelper.getExtraDataAsJson(data); - if (isFatal) { - throw new JavascriptException(JSStackTrace.format(message, stack)).setExtraDataAsJson(extraDataAsJson); - } else { - FLog.e(ReactConstants.TAG, JSStackTrace.format(message, stack)); - if (extraDataAsJson != null) { - FLog.d(ReactConstants.TAG, "extraData: %s", extraDataAsJson); - } + FLog.e(ReactConstants.TAG, JSStackTrace.format(message, stack)); + if (extraDataAsJson != null) { + FLog.d(ReactConstants.TAG, "extraData: %s", extraDataAsJson); } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaScriptTimerManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaScriptTimerExecutor.java similarity index 96% rename from android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaScriptTimerManager.java rename to android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaScriptTimerExecutor.java index 150fc6097555f..655d91985e151 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaScriptTimerManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaScriptTimerExecutor.java @@ -10,7 +10,7 @@ import com.facebook.react.bridge.WritableArray; /** An interface used by {@link JavaTimerManager} to access and call JS timers from Java. */ -public interface JavaScriptTimerManager { +public interface JavaScriptTimerExecutor { /** * Calls the JS callback(s) associated with the timer ID(s). Also unregisters the callback if the diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java index 00e2862a21626..c9329fb2f51e3 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java @@ -81,7 +81,7 @@ public void doFrame(long frameTimeNanos) { } if (mTimersToCall != null) { - mJavaScriptTimerManager.callTimers(mTimersToCall); + mJavaScriptTimerExecutor.callTimers(mTimersToCall); mTimersToCall = null; } @@ -139,7 +139,7 @@ public void run() { } if (sendIdleEvents) { - mJavaScriptTimerManager.callIdleCallbacks(absoluteFrameStartTime); + mJavaScriptTimerExecutor.callIdleCallbacks(absoluteFrameStartTime); } mCurrentIdleCallbackRunnable = null; @@ -151,7 +151,7 @@ public void cancel() { } private final ReactApplicationContext mReactApplicationContext; - private final JavaScriptTimerManager mJavaScriptTimerManager; + private final JavaScriptTimerExecutor mJavaScriptTimerExecutor; private final ReactChoreographer mReactChoreographer; private final DevSupportManager mDevSupportManager; private final Object mTimerGuard = new Object(); @@ -169,11 +169,11 @@ public void cancel() { public JavaTimerManager( ReactApplicationContext reactContext, - JavaScriptTimerManager javaScriptTimerManager, + JavaScriptTimerExecutor javaScriptTimerManager, ReactChoreographer reactChoreographer, DevSupportManager devSupportManager) { mReactApplicationContext = reactContext; - mJavaScriptTimerManager = javaScriptTimerManager; + mJavaScriptTimerExecutor = javaScriptTimerManager; mReactChoreographer = reactChoreographer; mDevSupportManager = devSupportManager; @@ -327,7 +327,7 @@ public void createAndMaybeCallTimer( if (mDevSupportManager.getDevSupportEnabled()) { long driftTime = Math.abs(remoteTime - deviceTime); if (driftTime > 60000) { - mJavaScriptTimerManager.emitTimeDriftWarning( + mJavaScriptTimerExecutor.emitTimeDriftWarning( "Debugger and device times have drifted by more than 60s. Please correct this by " + "running adb shell \"date `date +%m%d%H%M%Y.%S`\" on your debugger machine."); } @@ -338,7 +338,7 @@ public void createAndMaybeCallTimer( if (duration == 0 && !repeat) { WritableArray timerToCall = Arguments.createArray(); timerToCall.pushInt(callbackID); - mJavaScriptTimerManager.callTimers(timerToCall); + mJavaScriptTimerExecutor.callTimers(timerToCall); return; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java index 8ac7f07606a4a..94a8a5eca4c00 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java @@ -22,7 +22,7 @@ public final class TimingModule extends NativeTimingSpec implements LifecycleEventListener, HeadlessJsTaskEventListener { - public class BridgeTimerManager implements JavaScriptTimerManager { + public class BridgeTimerExecutor implements JavaScriptTimerExecutor { @Override public void callTimers(WritableArray timerIDs) { ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); @@ -61,7 +61,7 @@ public TimingModule(ReactApplicationContext reactContext, DevSupportManager devS mJavaTimerManager = new JavaTimerManager( reactContext, - new BridgeTimerManager(), + new BridgeTimerExecutor(), ReactChoreographer.getInstance(), devSupportManager); } @@ -129,11 +129,14 @@ public void onHeadlessJsTaskFinish(int taskId) { } @Override - public void onCatalystInstanceDestroy() { + public void invalidate() { + ReactApplicationContext reactApplicationContext = getReactApplicationContext(); + HeadlessJsTaskContext headlessJsTaskContext = - HeadlessJsTaskContext.getInstance(getReactApplicationContext()); + HeadlessJsTaskContext.getInstance(reactApplicationContext); headlessJsTaskContext.removeTaskEventListener(this); mJavaTimerManager.onInstanceDestroy(); + reactApplicationContext.removeLifecycleEventListener(this); } @VisibleForTesting diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK deleted file mode 100644 index 56be88ecc46b9..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK +++ /dev/null @@ -1,26 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") - -rn_android_library( - name = "datepicker", - srcs = glob(["**/*.java"]), - is_androidx = True, - labels = ["supermodule:xplat/default/public.react_native.infra"], - provided_deps = [ - react_native_dep("third-party/android/androidx:annotation"), - react_native_dep("third-party/android/androidx:core"), - react_native_dep("third-party/android/androidx:fragment"), - react_native_dep("third-party/android/androidx:legacy-support-core-ui"), - react_native_dep("third-party/android/androidx:legacy-support-core-utils"), - ], - visibility = [ - "PUBLIC", - ], - deps = [ - react_native_dep("third-party/java/infer-annotations:infer-annotations"), - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/common:common"), - react_native_target("java/com/facebook/react/module/annotations:annotations"), - ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], -) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogFragment.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogFragment.java deleted file mode 100644 index f3d190b4432e2..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogFragment.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.datepicker; - -import android.annotation.SuppressLint; -import android.app.DatePickerDialog; -import android.app.DatePickerDialog.OnDateSetListener; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.widget.DatePicker; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; -import java.util.Calendar; -import java.util.Locale; - -@SuppressLint("ValidFragment") -public class DatePickerDialogFragment extends DialogFragment { - - /** Minimum date supported by {@link DatePicker}, 01 Jan 1900 */ - private static final long DEFAULT_MIN_DATE = -2208988800001l; - - @Nullable private OnDateSetListener mOnDateSetListener; - @Nullable private OnDismissListener mOnDismissListener; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Bundle args = getArguments(); - return createDialog(args, getActivity(), mOnDateSetListener); - } - - /*package*/ static Dialog createDialog( - Bundle args, Context activityContext, @Nullable OnDateSetListener onDateSetListener) { - final Calendar c = Calendar.getInstance(); - if (args != null && args.containsKey(DatePickerDialogModule.ARG_DATE)) { - c.setTimeInMillis(args.getLong(DatePickerDialogModule.ARG_DATE)); - } - final int year = c.get(Calendar.YEAR); - final int month = c.get(Calendar.MONTH); - final int day = c.get(Calendar.DAY_OF_MONTH); - - DatePickerMode mode = DatePickerMode.DEFAULT; - if (args != null && args.getString(DatePickerDialogModule.ARG_MODE, null) != null) { - mode = - DatePickerMode.valueOf( - args.getString(DatePickerDialogModule.ARG_MODE).toUpperCase(Locale.US)); - } - - DatePickerDialog dialog = null; - - switch (mode) { - case CALENDAR: - dialog = - new DismissableDatePickerDialog( - activityContext, - activityContext - .getResources() - .getIdentifier( - "CalendarDatePickerDialog", "style", activityContext.getPackageName()), - onDateSetListener, - year, - month, - day); - break; - case SPINNER: - dialog = - new DismissableDatePickerDialog( - activityContext, - android.R.style.Theme_Holo_Light_Dialog, - onDateSetListener, - year, - month, - day); - dialog - .getWindow() - .setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT)); - break; - case DEFAULT: - dialog = - new DismissableDatePickerDialog(activityContext, onDateSetListener, year, month, day); - break; - } - - final DatePicker datePicker = dialog.getDatePicker(); - - if (args != null && args.containsKey(DatePickerDialogModule.ARG_MINDATE)) { - // Set minDate to the beginning of the day. We need this because of clowniness in datepicker - // that causes it to throw an exception if minDate is greater than the internal timestamp - // that it generates from the y/m/d passed in the constructor. - c.setTimeInMillis(args.getLong(DatePickerDialogModule.ARG_MINDATE)); - c.set(Calendar.HOUR_OF_DAY, 0); - c.set(Calendar.MINUTE, 0); - c.set(Calendar.SECOND, 0); - c.set(Calendar.MILLISECOND, 0); - datePicker.setMinDate(c.getTimeInMillis()); - } else { - // This is to work around a bug in DatePickerDialog where it doesn't display a title showing - // the date under certain conditions. - datePicker.setMinDate(DEFAULT_MIN_DATE); - } - if (args != null && args.containsKey(DatePickerDialogModule.ARG_MAXDATE)) { - // Set maxDate to the end of the day, same reason as for minDate. - c.setTimeInMillis(args.getLong(DatePickerDialogModule.ARG_MAXDATE)); - c.set(Calendar.HOUR_OF_DAY, 23); - c.set(Calendar.MINUTE, 59); - c.set(Calendar.SECOND, 59); - c.set(Calendar.MILLISECOND, 999); - datePicker.setMaxDate(c.getTimeInMillis()); - } - - return dialog; - } - - @Override - public void onDismiss(DialogInterface dialog) { - super.onDismiss(dialog); - if (mOnDismissListener != null) { - mOnDismissListener.onDismiss(dialog); - } - } - - /*package*/ void setOnDateSetListener(@Nullable OnDateSetListener onDateSetListener) { - mOnDateSetListener = onDateSetListener; - } - - /*package*/ void setOnDismissListener(@Nullable OnDismissListener onDismissListener) { - mOnDismissListener = onDismissListener; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogModule.java deleted file mode 100644 index 20cb825893816..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DatePickerDialogModule.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.datepicker; - -import android.app.Activity; -import android.app.DatePickerDialog.OnDateSetListener; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.os.Bundle; -import android.widget.DatePicker; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import com.facebook.fbreact.specs.NativeDatePickerAndroidSpec; -import com.facebook.react.bridge.*; -import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.module.annotations.ReactModule; - -/** - * {@link NativeModule} that allows JS to show a native date picker dialog and get called back when - * the user selects a date. - */ -@ReactModule(name = DatePickerDialogModule.FRAGMENT_TAG) -public class DatePickerDialogModule extends NativeDatePickerAndroidSpec { - - @VisibleForTesting public static final String FRAGMENT_TAG = "DatePickerAndroid"; - - private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY"; - - /* package */ static final String ARG_DATE = "date"; - /* package */ static final String ARG_MINDATE = "minDate"; - /* package */ static final String ARG_MAXDATE = "maxDate"; - /* package */ static final String ARG_MODE = "mode"; - - /* package */ static final String ACTION_DATE_SET = "dateSetAction"; - /* package */ static final String ACTION_DISMISSED = "dismissedAction"; - - public DatePickerDialogModule(ReactApplicationContext reactContext) { - super(reactContext); - } - - public @NonNull String getName() { - return DatePickerDialogModule.FRAGMENT_TAG; - } - - private class DatePickerDialogListener implements OnDateSetListener, OnDismissListener { - - private final Promise mPromise; - private boolean mPromiseResolved = false; - - public DatePickerDialogListener(final Promise promise) { - mPromise = promise; - } - - @Override - public void onDateSet(DatePicker view, int year, int month, int day) { - if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) { - WritableMap result = new WritableNativeMap(); - result.putString("action", ACTION_DATE_SET); - result.putInt("year", year); - result.putInt("month", month); - result.putInt("day", day); - mPromise.resolve(result); - mPromiseResolved = true; - } - } - - @Override - public void onDismiss(DialogInterface dialog) { - if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) { - WritableMap result = new WritableNativeMap(); - result.putString("action", ACTION_DISMISSED); - mPromise.resolve(result); - mPromiseResolved = true; - } - } - } - - /** - * Show a date picker dialog. - * - * @param options a map containing options. Available keys are: - *

    - *
  • {@code date} (timestamp in milliseconds) the date to show by default - *
  • {@code minDate} (timestamp in milliseconds) the minimum date the user should be - * allowed to select - *
  • {@code maxDate} (timestamp in milliseconds) the maximum date the user should be - * allowed to select - *
  • {@code mode} To set the date picker mode to 'calendar/spinner/default' - *
- * - * @param promise This will be invoked with parameters action, year, month (0-11), day, where - * action is {@code dateSetAction} or {@code dismissedAction}, depending on what the user did. - * If the action is dismiss, year, month and date are undefined. - */ - @Override - public void open(@Nullable final ReadableMap options, Promise promise) { - Activity raw_activity = getCurrentActivity(); - if (raw_activity == null || !(raw_activity instanceof FragmentActivity)) { - promise.reject( - ERROR_NO_ACTIVITY, - "Tried to open a DatePicker dialog while not attached to a FragmentActivity"); - return; - } - - FragmentActivity activity = (FragmentActivity) raw_activity; - - FragmentManager fragmentManager = activity.getSupportFragmentManager(); - DialogFragment oldFragment = (DialogFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG); - if (oldFragment != null) { - oldFragment.dismiss(); - } - DatePickerDialogFragment fragment = new DatePickerDialogFragment(); - if (options != null) { - final Bundle args = createFragmentArguments(options); - fragment.setArguments(args); - } - final DatePickerDialogListener listener = new DatePickerDialogListener(promise); - fragment.setOnDismissListener(listener); - fragment.setOnDateSetListener(listener); - fragment.show(fragmentManager, FRAGMENT_TAG); - } - - private Bundle createFragmentArguments(ReadableMap options) { - final Bundle args = new Bundle(); - if (options.hasKey(ARG_DATE) && !options.isNull(ARG_DATE)) { - args.putLong(ARG_DATE, (long) options.getDouble(ARG_DATE)); - } - if (options.hasKey(ARG_MINDATE) && !options.isNull(ARG_MINDATE)) { - args.putLong(ARG_MINDATE, (long) options.getDouble(ARG_MINDATE)); - } - if (options.hasKey(ARG_MAXDATE) && !options.isNull(ARG_MAXDATE)) { - args.putLong(ARG_MAXDATE, (long) options.getDouble(ARG_MAXDATE)); - } - if (options.hasKey(ARG_MODE) && !options.isNull(ARG_MODE)) { - args.putString(ARG_MODE, options.getString(ARG_MODE)); - } - return args; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DismissableDatePickerDialog.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DismissableDatePickerDialog.java deleted file mode 100644 index cfcc1fd3c7748..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DismissableDatePickerDialog.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.datepicker; - -import android.app.DatePickerDialog; -import android.content.Context; -import android.content.res.TypedArray; -import android.os.Build; -import android.util.AttributeSet; -import android.widget.DatePicker; -import androidx.annotation.Nullable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -public class DismissableDatePickerDialog extends DatePickerDialog { - - public DismissableDatePickerDialog( - Context context, - @Nullable DatePickerDialog.OnDateSetListener callback, - int year, - int monthOfYear, - int dayOfMonth) { - super(context, callback, year, monthOfYear, dayOfMonth); - fixSpinner(context, year, monthOfYear, dayOfMonth); - } - - public DismissableDatePickerDialog( - Context context, - int theme, - @Nullable DatePickerDialog.OnDateSetListener callback, - int year, - int monthOfYear, - int dayOfMonth) { - super(context, theme, callback, year, monthOfYear, dayOfMonth); - fixSpinner(context, year, monthOfYear, dayOfMonth); - } - - private void fixSpinner(Context context, int year, int month, int dayOfMonth) { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) { - try { - // Get the theme's android:datePickerMode - final int MODE_SPINNER = 2; - Class styleableClass = Class.forName("com.android.internal.R$styleable"); - Field datePickerStyleableField = styleableClass.getField("DatePicker"); - int[] datePickerStyleable = (int[]) datePickerStyleableField.get(null); - - final TypedArray a = - context.obtainStyledAttributes( - null, datePickerStyleable, android.R.attr.datePickerStyle, 0); - Field datePickerModeStyleableField = styleableClass.getField("DatePicker_datePickerMode"); - int datePickerModeStyleable = datePickerModeStyleableField.getInt(null); - final int mode = a.getInt(datePickerModeStyleable, MODE_SPINNER); - a.recycle(); - - if (mode == MODE_SPINNER) { - DatePicker datePicker = - (DatePicker) - findField(DatePickerDialog.class, DatePicker.class, "mDatePicker").get(this); - Class delegateClass = Class.forName("android.widget.DatePickerSpinnerDelegate"); - Field delegateField = findField(DatePicker.class, delegateClass, "mDelegate"); - Object delegate = delegateField.get(datePicker); - Class spinnerDelegateClass; - spinnerDelegateClass = Class.forName("android.widget.DatePickerSpinnerDelegate"); - - // In 7.0 Nougat for some reason the datePickerMode is ignored and the delegate is - // DatePickerClockDelegate - if (delegate.getClass() != spinnerDelegateClass) { - delegateField.set(datePicker, null); // throw out the DatePickerClockDelegate! - datePicker.removeAllViews(); // remove the DatePickerClockDelegate views - Method createSpinnerUIDelegate = - DatePicker.class.getDeclaredMethod( - "createSpinnerUIDelegate", - Context.class, - AttributeSet.class, - int.class, - int.class); - createSpinnerUIDelegate.setAccessible(true); - - // Instantiate a DatePickerSpinnerDelegate throughout createSpinnerUIDelegate method - delegate = - createSpinnerUIDelegate.invoke( - datePicker, context, null, android.R.attr.datePickerStyle, 0); - delegateField.set( - datePicker, delegate); // set the DatePicker.mDelegate to the spinner delegate - datePicker.setCalendarViewShown(false); - // Initialize the date for the DatePicker delegate again - datePicker.init(year, month, dayOfMonth, this); - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - private static Field findField(Class objectClass, Class fieldClass, String expectedName) { - try { - Field field = objectClass.getDeclaredField(expectedName); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException e) { - } // ignore - // search for it if it wasn't found under the expected ivar name - for (Field searchField : objectClass.getDeclaredFields()) { - if (searchField.getType() == fieldClass) { - searchField.setAccessible(true); - return searchField; - } - } - return null; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/AnimationsDebugModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/AnimationsDebugModule.java index da69cf93c05de..b803f0d46b49b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/AnimationsDebugModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/AnimationsDebugModule.java @@ -103,7 +103,7 @@ public void stopRecordingFps(double animationStopTimeMs) { } @Override - public void onCatalystInstanceDestroy() { + public void invalidate() { if (mFrameCallback != null) { mFrameCallback.stop(); mFrameCallback = null; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK index dd5f58313cd3b..ec954ab8a841c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "debug", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -23,13 +24,14 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager:uimanager"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) rn_android_library( name = "interfaces", srcs = glob(["interfaces/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK index c4f485bd0995f..f6418a9293641 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "deviceinfo", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -19,6 +20,6 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager:uimanager"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java index 083c9c1d9f27b..8e7b79822104f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.java @@ -13,9 +13,9 @@ import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.uimanager.DisplayMetricsHolder; @@ -54,8 +54,13 @@ public String getName() { @Override public @Nullable Map getTypedExportedConstants() { + WritableMap displayMetrics = DisplayMetricsHolder.getDisplayMetricsWritableMap(mFontScale); + + // Cache the initial dimensions for later comparison in emitUpdateDimensionsEvent + mPreviousDisplayMetrics = displayMetrics.copy(); + HashMap constants = new HashMap<>(); - constants.put("Dimensions", DisplayMetricsHolder.getDisplayMetricsMap(mFontScale)); + constants.put("Dimensions", displayMetrics.toHashMap()); return constants; } @@ -83,10 +88,9 @@ public void emitUpdateDimensionsEvent() { return; } - if (mReactApplicationContext.hasActiveCatalystInstance()) { + if (mReactApplicationContext.hasActiveReactInstance()) { // Don't emit an event to JS if the dimensions haven't changed - WritableNativeMap displayMetrics = - DisplayMetricsHolder.getDisplayMetricsNativeMap(mFontScale); + WritableMap displayMetrics = DisplayMetricsHolder.getDisplayMetricsWritableMap(mFontScale); if (mPreviousDisplayMetrics == null) { mPreviousDisplayMetrics = displayMetrics.copy(); } else if (!displayMetrics.equals(mPreviousDisplayMetrics)) { @@ -96,7 +100,7 @@ public void emitUpdateDimensionsEvent() { .emit("didUpdateDimensions", displayMetrics); } } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( NAME, new ReactNoCrashSoftException( "No active CatalystInstance, cannot emitUpdateDimensionsEvent")); @@ -104,5 +108,12 @@ public void emitUpdateDimensionsEvent() { } @Override - public void invalidate() {} + public void invalidate() { + super.invalidate(); + + ReactApplicationContext applicationContext = getReactApplicationContextIfActiveOrWarn(); + if (applicationContext != null) { + applicationContext.removeLifecycleEventListener(this); + } + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK index 60eb0750f5082..b6fd3e2ab37ba 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "dialog", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -23,5 +24,5 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java index cede08658f183..d10716cf25367 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java @@ -141,7 +141,7 @@ public AlertFragmentListener(Callback callback) { @Override public void onClick(DialogInterface dialog, int which) { if (!mCallbackConsumed) { - if (getReactApplicationContext().isBridgeless() || getReactApplicationContext().hasActiveCatalystInstance()) { + if (getReactApplicationContext().hasActiveReactInstance()) { mCallback.invoke(ACTION_BUTTON_CLICKED, which); mCallbackConsumed = true; } @@ -151,7 +151,7 @@ public void onClick(DialogInterface dialog, int which) { @Override public void onDismiss(DialogInterface dialog) { if (!mCallbackConsumed) { - if (getReactApplicationContext().isBridgeless() || getReactApplicationContext().hasActiveCatalystInstance()) { + if (getReactApplicationContext().hasActiveReactInstance()) { mCallback.invoke(ACTION_DISMISSED); mCallbackConsumed = true; } @@ -254,4 +254,13 @@ private FragmentManagerHelper getFragmentManagerHelper() { } return new FragmentManagerHelper(((FragmentActivity) activity).getSupportFragmentManager()); } + + @Override + public void invalidate() { + super.invalidate(); + ReactApplicationContext applicationContext = getReactApplicationContextIfActiveOrWarn(); + if (applicationContext != null) { + applicationContext.removeLifecycleEventListener(this); + } + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK index f88d3d22465bd..361c17d91f7af 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_target", "rn_android_li rn_android_library( name = "fabric", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK index a0762971a030e..5f4e8ce6a5815 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "fresco", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java index ac70eff2adcc1..2dc27d679cc30 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java @@ -194,4 +194,14 @@ private ImagePipeline getImagePipeline() { } return mImagePipeline; } + + @Override + public void invalidate() { + super.invalidate(); + + ReactApplicationContext applicationContext = getReactApplicationContextIfActiveOrWarn(); + if (applicationContext != null) { + applicationContext.removeLifecycleEventListener(this); + } + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK index 2b7eeb854d942..b4a4ad13575d9 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "i18nmanager", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -21,5 +22,6 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/I18nManagerModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/I18nManagerModule.java index 9567c145b2a70..c2c54b93df4bb 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/I18nManagerModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/I18nManagerModule.java @@ -8,9 +8,10 @@ package com.facebook.react.modules.i18nmanager; import android.content.Context; -import com.facebook.react.bridge.ContextBaseJavaModule; +import android.os.Build; +import com.facebook.fbreact.specs.NativeI18nManagerSpec; import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.common.MapBuilder; import com.facebook.react.module.annotations.ReactModule; import java.util.Locale; @@ -18,13 +19,13 @@ /** {@link NativeModule} that allows JS to set allowRTL and get isRTL status. */ @ReactModule(name = I18nManagerModule.NAME) -public class I18nManagerModule extends ContextBaseJavaModule { +public class I18nManagerModule extends NativeI18nManagerSpec { public static final String NAME = "I18nManager"; private final I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance(); - public I18nManagerModule(Context context) { + public I18nManagerModule(ReactApplicationContext context) { super(context); } @@ -34,9 +35,14 @@ public String getName() { } @Override - public Map getConstants() { - final Context context = getContext(); - final Locale locale = context.getResources().getConfiguration().locale; + public Map getTypedExportedConstants() { + final Context context = getReactApplicationContext(); + final Locale locale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + locale = context.getResources().getConfiguration().getLocales().get(0); + } else { + locale = context.getResources().getConfiguration().locale; + } final Map constants = MapBuilder.newHashMap(); constants.put("isRTL", sharedI18nUtilInstance.isRTL(context)); @@ -46,18 +52,18 @@ public Map getConstants() { return constants; } - @ReactMethod + @Override public void allowRTL(boolean value) { - sharedI18nUtilInstance.allowRTL(getContext(), value); + sharedI18nUtilInstance.allowRTL(getReactApplicationContext(), value); } - @ReactMethod + @Override public void forceRTL(boolean value) { - sharedI18nUtilInstance.forceRTL(getContext(), value); + sharedI18nUtilInstance.forceRTL(getReactApplicationContext(), value); } - @ReactMethod + @Override public void swapLeftAndRightInRTL(boolean value) { - sharedI18nUtilInstance.swapLeftAndRightInRTL(getContext(), value); + sharedI18nUtilInstance.swapLeftAndRightInRTL(getReactApplicationContext(), value); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK index 7a5ef484c3d95..7ebd194664fa5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "image", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -24,6 +25,6 @@ rn_android_library( react_native_target("java/com/facebook/react/views/image:image"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK index ef9571e84e6de..272ca10b507d4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "intent", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -16,5 +17,5 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java index 41383e528dc3c..98377c76f7909 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java @@ -86,25 +86,8 @@ public void openURL(String url, Promise promise) { } try { - Activity currentActivity = getCurrentActivity(); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url).normalizeScheme()); - - String selfPackageName = getReactApplicationContext().getPackageName(); - ComponentName componentName = - intent.resolveActivity(getReactApplicationContext().getPackageManager()); - String otherPackageName = (componentName != null ? componentName.getPackageName() : ""); - - // If there is no currentActivity or we are launching to a different package we need to set - // the FLAG_ACTIVITY_NEW_TASK flag - if (currentActivity == null || !selfPackageName.equals(otherPackageName)) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - - if (currentActivity != null) { - currentActivity.startActivity(intent); - } else { - getReactApplicationContext().startActivity(intent); - } + sendOSIntent(intent, false); promise.resolve(true); } catch (Exception e) { @@ -235,6 +218,27 @@ public void sendIntent(String action, @Nullable ReadableArray extras, Promise pr } } - getReactApplicationContext().startActivity(intent); + sendOSIntent(intent, true); + } + + private void sendOSIntent(Intent intent, Boolean useNewTaskFlag) { + Activity currentActivity = getCurrentActivity(); + + String selfPackageName = getReactApplicationContext().getPackageName(); + ComponentName componentName = + intent.resolveActivity(getReactApplicationContext().getPackageManager()); + String otherPackageName = (componentName != null ? componentName.getPackageName() : ""); + + // If there is no currentActivity or we are launching to a different package we need to set + // the FLAG_ACTIVITY_NEW_TASK flag + if (useNewTaskFlag || currentActivity == null || !selfPackageName.equals(otherPackageName)) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + if (currentActivity != null) { + currentActivity.startActivity(intent); + } else { + getReactApplicationContext().startActivity(intent); + } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK index 592da58ca8362..c7464855ecb0e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "network", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -28,5 +29,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/ForwardingCookieHandler.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/ForwardingCookieHandler.java index 63a9fe7ab5a29..529af0090f538 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/ForwardingCookieHandler.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/ForwardingCookieHandler.java @@ -144,10 +144,7 @@ protected void doInBackgroundGuarded(Void... params) { // specific exception. // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/webkit/WebViewFactory.java#348 if (message != null - && exception - .getClass() - .getCanonicalName() - .equals("android.webkit.WebViewFactory.MissingWebViewPackageException")) { + && exception.getClass().getCanonicalName().contains("MissingWebViewPackageException")) { return null; } else { throw exception; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java index 5327b11f21641..37cefedf0d6ff 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java @@ -203,7 +203,7 @@ public String getName() { } @Override - public void onCatalystInstanceDestroy() { + public void invalidate() { mShuttingDown = true; cancelAllRequests(); mCookieHandler.destroy(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java index 278f306ae709f..d79a52b26b862 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java @@ -9,8 +9,6 @@ import android.content.Context; import androidx.annotation.Nullable; import java.io.File; -import java.security.Provider; -import java.security.Security; import java.util.concurrent.TimeUnit; import okhttp3.Cache; import okhttp3.OkHttpClient; @@ -61,13 +59,7 @@ public static OkHttpClient createClient(Context context) { public static OkHttpClient.Builder createClientBuilder() { // No timeouts by default OkHttpClient.Builder client = new OkHttpClient.Builder().connectTimeout(0, TimeUnit.MILLISECONDS).readTimeout(0, TimeUnit.MILLISECONDS).writeTimeout(0, TimeUnit.MILLISECONDS).cookieJar(new ReactCookieJarContainer()); - try { - Class ConscryptProvider = Class.forName("org.conscrypt.OpenSSLProvider"); - Security.insertProviderAt((Provider) ConscryptProvider.newInstance(), 1); - return client; - } catch (Exception e) { - return enableTls12OnPreLollipop(client); - } + return client; } public static OkHttpClient.Builder createClientBuilder(Context context) { @@ -85,13 +77,4 @@ public static OkHttpClient.Builder createClientBuilder(Context context, int cach Cache cache = new Cache(cacheDirectory, cacheSize); return client.cache(cache); } - - /* - On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are - available but not enabled by default. The following method - enables it. - */ - public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) { - return client; - } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK index 3aa6d043e9e63..78b11364532c3 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "permissions", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -15,5 +16,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK index 8b788efbcc631..840c4ec62011f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "share", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -15,5 +16,5 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK index a3a401b9b2bba..6d99c374a7506 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "sound", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -19,5 +20,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager:uimanager"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK index fd0d6b732a25f..100b151cc86ee 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "statusbar", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -24,5 +25,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager:uimanager"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/AsyncStorageModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/AsyncStorageModule.java index eb6b72a4d9466..675e43c3907a4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/AsyncStorageModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/AsyncStorageModule.java @@ -103,7 +103,7 @@ public void initialize() { } @Override - public void onCatalystInstanceDestroy() { + public void invalidate() { mShuttingDown = true; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK index 5f54664eec412..56593871d77c6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "storage", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -18,5 +19,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/common:common"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK index 9170b3c036b9c..5f412e628cbee 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK @@ -6,6 +6,7 @@ rn_android_library( "AndroidInfoModule.java", "ReactNativeVersion.java", ], + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -21,7 +22,7 @@ rn_android_library( react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ":systeminfo-moduleless", ], ) @@ -31,6 +32,7 @@ rn_android_library( srcs = [ "AndroidInfoHelpers.java", ], + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -43,5 +45,5 @@ rn_android_library( react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("res:systeminfo"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java b/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java index 2c346113d8f2c..ff4dd7632008b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java @@ -1,5 +1,5 @@ /** - * @generated by scripts/bump-oss-version.js + * @generated by scripts/set-rn-version.js * * Copyright (c) Facebook, Inc. and its affiliates. * @@ -16,7 +16,7 @@ public class ReactNativeVersion { public static final Map VERSION = MapBuilder.of( "major", 0, - "minor", 64, + "minor", 67, "patch", 2, "prerelease", null); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK index dc3d6cb5c6a3b..9a1b43c9ef51d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "toast", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -14,5 +15,5 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK index 40c531ec051ae..b29994922211c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "vibration", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", @@ -16,5 +17,5 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), ], - exported_deps = [react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec")], + exported_deps = [react_native_root_target(":FBReactNativeSpec")], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK index 990b45769e2da..378a516408784 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_roo rn_android_library( name = "websocket", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -22,6 +23,6 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/network:network"), ], exported_deps = [ - react_native_root_target("Libraries:generated_java_modules-FBReactNativeSpec"), + react_native_root_target(":FBReactNativeSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK index 3aa924ecad3ad..76c5775ea9d04 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK @@ -5,6 +5,7 @@ rn_android_library( srcs = glob( ["**/*.java"], ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java b/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java index 99cb114823169..69a417078f9f7 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.java @@ -41,6 +41,7 @@ public interface ConnectionCallback { private final String mUrl; private final Handler mHandler; + private final OkHttpClient mOkHttpClient; private boolean mClosed = false; private boolean mSuppressConnectionErrors; private @Nullable WebSocket mWebSocket; @@ -48,12 +49,20 @@ public interface ConnectionCallback { private @Nullable ConnectionCallback mConnectionCallback; public ReconnectingWebSocket( - String url, MessageCallback messageCallback, ConnectionCallback connectionCallback) { + String url, + @Nullable MessageCallback messageCallback, + @Nullable ConnectionCallback connectionCallback) { super(); mUrl = url; mMessageCallback = messageCallback; mConnectionCallback = connectionCallback; mHandler = new Handler(Looper.getMainLooper()); + mOkHttpClient = + new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read + .build(); } public void connect() { @@ -61,15 +70,8 @@ public void connect() { throw new IllegalStateException("Can't connect closed client"); } - OkHttpClient httpClient = - new OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read - .build(); - Request request = new Request.Builder().url(mUrl).build(); - httpClient.newWebSocket(request, this); + mOkHttpClient.newWebSocket(request, this); } private synchronized void delayedReconnect() { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java b/android/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java index 5cee5dba148d3..9ac4470224519 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java @@ -59,7 +59,7 @@ /** * This annotation processor crawls subclasses of ReactShadowNode and ViewManager and finds their * exported properties with the @ReactProp or @ReactGroupProp annotation. It generates a class per - * shadow node/view manager that is named {@code $$PropSetter}. This class contains + * shadow node/view manager that is named {@code $$PropsSetter}. This class contains * methods to retrieve the name and type of all methods and a way to set these properties without * reflection. */ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK index d8d99e703d2b0..3575b09bb06b1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/BUCK @@ -7,6 +7,7 @@ rn_android_library( "*.java", ], ), + autoglob = False, labels = [ "supermodule:xplat/default/public.react_native.infra", ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk index c44496918d50f..fd0ff0d7fd64b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/Android.mk @@ -13,7 +13,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/reactperflogger # Header search path for modules that depend on this module LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_LDLIBS += -landroid diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK index bd6a96575ac35..956b28f2a664a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/reactperflogger/jni/BUCK @@ -9,12 +9,6 @@ rn_xplat_cxx_library( exported_headers = { "reactperflogger/JNativeModulePerfLogger.h": "reactperflogger/JNativeModulePerfLogger.h", }, - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbandroid_allow_jni_merging = True, fbandroid_labels = [ "supermodule:xplat/default/public.react_native.infra", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK index e8e27ee8e2b90..84138c4394d91 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "shell", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -33,7 +34,6 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/camera:camera"), react_native_target("java/com/facebook/react/modules/clipboard:clipboard"), react_native_target("java/com/facebook/react/modules/core:core"), - react_native_target("java/com/facebook/react/modules/datepicker:datepicker"), react_native_target("java/com/facebook/react/modules/debug:debug"), react_native_target("java/com/facebook/react/modules/dialog:dialog"), react_native_target("java/com/facebook/react/modules/fresco:fresco"), @@ -54,7 +54,6 @@ rn_android_library( react_native_target("java/com/facebook/react/views/drawer:drawer"), react_native_target("java/com/facebook/react/views/image:image"), react_native_target("java/com/facebook/react/views/modal:modal"), - react_native_target("java/com/facebook/react/views/picker:picker"), react_native_target("java/com/facebook/react/views/progressbar:progressbar"), react_native_target("java/com/facebook/react/views/scroll:scroll"), react_native_target("java/com/facebook/react/views/slider:slider"), diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/android/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index 471ba0c833ec4..34dc28005784d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -23,7 +23,6 @@ import com.facebook.react.modules.blob.FileReaderModule; import com.facebook.react.modules.camera.ImageStoreManager; import com.facebook.react.modules.clipboard.ClipboardModule; -import com.facebook.react.modules.datepicker.DatePickerDialogModule; import com.facebook.react.modules.dialog.DialogModule; import com.facebook.react.modules.fresco.FrescoModule; import com.facebook.react.modules.i18nmanager.I18nManagerModule; @@ -43,8 +42,6 @@ import com.facebook.react.views.drawer.ReactDrawerLayoutManager; import com.facebook.react.views.image.ReactImageManager; import com.facebook.react.views.modal.ReactModalHostManager; -import com.facebook.react.views.picker.ReactDialogPickerManager; -import com.facebook.react.views.picker.ReactDropdownPickerManager; import com.facebook.react.views.progressbar.ReactProgressBarViewManager; import com.facebook.react.views.scroll.ReactHorizontalScrollContainerViewManager; import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager; @@ -74,7 +71,6 @@ FileReaderModule.class, AsyncStorageModule.class, ClipboardModule.class, - DatePickerDialogModule.class, DialogModule.class, FrescoModule.class, I18nManagerModule.class, @@ -119,8 +115,6 @@ public MainReactPackage(MainPackageConfig config) { return new AsyncStorageModule(context); case ClipboardModule.NAME: return new ClipboardModule(context); - case DatePickerDialogModule.FRAGMENT_TAG: - return new DatePickerDialogModule(context); case DialogModule.NAME: return new DialogModule(context); case FrescoModule.NAME: @@ -160,9 +154,7 @@ public MainReactPackage(MainPackageConfig config) { public List createViewManagers(ReactApplicationContext reactContext) { List viewManagers = new ArrayList<>(); - viewManagers.add(new ReactDialogPickerManager()); viewManagers.add(new ReactDrawerLayoutManager()); - viewManagers.add(new ReactDropdownPickerManager()); viewManagers.add(new ReactHorizontalScrollViewManager()); viewManagers.add(new ReactHorizontalScrollContainerViewManager()); viewManagers.add(new ReactProgressBarViewManager()); @@ -203,7 +195,6 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { FileReaderModule.class, AsyncStorageModule.class, ClipboardModule.class, - DatePickerDialogModule.class, DialogModule.class, FrescoModule.class, I18nManagerModule.class, diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK index beb35ce40c105..6a21268b9c579 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "surface", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK index 839bd5beb44bb..c9d6bbbe59e0a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "touch", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK index 721fe18d0cc7f..5017297bed814 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK @@ -8,6 +8,7 @@ rn_android_library( ], exclude = ["CallInvokerHolderImpl.java"], ), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ @@ -28,7 +29,6 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:interfaces"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/config:config"), - react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/module/model:model"), ":callinvokerholder", ], @@ -40,6 +40,7 @@ rn_android_library( rn_android_library( name = "callinvokerholder", srcs = ["CallInvokerHolderImpl.java"], + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java index e6e6fcce69e09..6a592ae02fb01 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java @@ -15,8 +15,8 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.CxxModuleWrapper; import com.facebook.react.bridge.JSIModule; -import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.RuntimeExecutor; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder; import com.facebook.react.turbomodule.core.interfaces.TurboModule; import com.facebook.react.turbomodule.core.interfaces.TurboModuleRegistry; @@ -49,18 +49,19 @@ public class TurboModuleManager implements JSIModule, TurboModuleRegistry { private final HybridData mHybridData; public TurboModuleManager( - JavaScriptContextHolder jsContext, + RuntimeExecutor runtimeExecutor, @Nullable final TurboModuleManagerDelegate delegate, CallInvokerHolder jsCallInvokerHolder, CallInvokerHolder nativeCallInvokerHolder) { maybeLoadSoLibrary(); mHybridData = initHybrid( - jsContext.get(), + runtimeExecutor, (CallInvokerHolderImpl) jsCallInvokerHolder, (CallInvokerHolderImpl) nativeCallInvokerHolder, delegate, - false); + ReactFeatureFlags.useGlobalCallbackCleanupScopeUsingRetainJSCallback, + ReactFeatureFlags.useTurboModuleManagerCallbackCleanupScope); installJSIBindings(); mEagerInitModuleNames = @@ -215,7 +216,7 @@ private TurboModule getModule( * NativeModules should be initialized after ReactApplicationContext has been set up. * Therefore, we should initialize on the TurboModule now. */ - ((NativeModule) turboModule).initialize(); + turboModule.initialize(); } TurboModulePerfLogger.moduleCreateSetUpEnd(moduleName, moduleHolder.getModuleId()); @@ -289,11 +290,12 @@ public boolean hasModule(String moduleName) { } private native HybridData initHybrid( - long jsContext, + RuntimeExecutor runtimeExecutor, CallInvokerHolderImpl jsCallInvokerHolder, CallInvokerHolderImpl nativeCallInvokerHolder, TurboModuleManagerDelegate tmmDelegate, - boolean enablePromiseAsyncDispatch); + boolean useGlobalCallbackCleanupScopeUsingRetainJSCallback, + boolean useTurboModuleManagerCallbackCleanupScope); private native void installJSIBindings(); @@ -328,8 +330,7 @@ public void onCatalystInstanceDestroy() { final TurboModule turboModule = getModule(moduleName, moduleHolder, false); if (turboModule != null) { - // TODO(T48014458): Rename this to invalidate() - ((NativeModule) turboModule).onCatalystInstanceDestroy(); + turboModule.invalidate(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManagerDelegate.java index b744d110dd969..9dcd6126e0c3c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManagerDelegate.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManagerDelegate.java @@ -9,6 +9,7 @@ import androidx.annotation.Nullable; import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.CxxModuleWrapper; import com.facebook.react.turbomodule.core.interfaces.TurboModule; import com.facebook.soloader.SoLoader; @@ -16,6 +17,8 @@ import java.util.List; public abstract class TurboModuleManagerDelegate { + @DoNotStrip + @SuppressWarnings("unused") private final HybridData mHybridData; private static volatile boolean sIsSoLibraryLoaded; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java index eb8e49d5dfdae..36d7d1d010c4d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModulePerfLogger.java @@ -7,10 +7,12 @@ package com.facebook.react.turbomodule.core; +import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.perflogger.NativeModulePerfLogger; import com.facebook.soloader.SoLoader; import javax.annotation.Nullable; +@DoNotStrip public class TurboModulePerfLogger { @Nullable private static NativeModulePerfLogger sNativeModulePerfLogger = null; private static boolean sIsSoLibraryLoaded = false; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK index ad97b93f20d94..5526d046ddfef 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK @@ -5,6 +5,7 @@ rn_android_library( srcs = glob( ["*.java"], ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.java index 13bbe58717984..6c80d059f5a35 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModule.java @@ -9,9 +9,9 @@ /** All turbo modules should inherit from this interface */ public interface TurboModule { - /** - * When CatalystInstance is destroyed, this method will be called. All implementing TurboModules - * can perform cleanup here. - */ + /** Initialize the TurboModule. */ + void initialize(); + + /** Called before React Native is torn down. Clean up after the TurboModule. */ void invalidate(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk index 955cc86a8b10d..b017546b19db1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk @@ -17,11 +17,11 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon # Header search path for modules that depend on this module LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall -LOCAL_SHARED_LIBRARIES = libfb libfbjni +LOCAL_SHARED_LIBRARIES = libfb libfbjni libreactnativeutilsjni -LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni +LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni libruntimeexecutor # Name of this module. LOCAL_MODULE := callinvokerholder @@ -48,7 +48,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon # Header search path for modules that depend on this module LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_SHARED_LIBRARIES = libfb libfbjni libreact_nativemodule_core diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK index b0db877eea667..4291e2ae40f11 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK @@ -11,12 +11,6 @@ rn_xplat_cxx_library( "ReactCommon/TurboModuleManager.h": "ReactCommon/TurboModuleManager.h", "ReactCommon/TurboModuleManagerDelegate.h": "ReactCommon/TurboModuleManagerDelegate.h", }, - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, preprocessor_flags = [ @@ -35,6 +29,7 @@ rn_xplat_cxx_library( ":callinvokerholder", react_native_xplat_shared_library_target("jsi:jsi"), react_native_xplat_target("react/nativemodule/core:core"), + react_native_xplat_target("runtimeexecutor:runtimeexecutor"), react_native_target("java/com/facebook/react/reactperflogger/jni:jni"), ], ) @@ -48,12 +43,6 @@ rn_xplat_cxx_library( exported_headers = { "ReactCommon/CallInvokerHolder.h": "ReactCommon/CallInvokerHolder.h", }, - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbandroid_deps = [ FBJNI_TARGET, ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp index 131b41b075ecb..ce7a43e0c91a2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp @@ -23,35 +23,58 @@ namespace react { TurboModuleManager::TurboModuleManager( jni::alias_ref jThis, - jsi::Runtime *rt, + RuntimeExecutor runtimeExecutor, std::shared_ptr jsCallInvoker, std::shared_ptr nativeCallInvoker, - jni::alias_ref delegate) + jni::alias_ref delegate, + bool useGlobalCallbackCleanupScopeUsingRetainJSCallback, + bool useTurboModuleManagerCallbackCleanupScope) : javaPart_(jni::make_global(jThis)), - runtime_(rt), + runtimeExecutor_(runtimeExecutor), jsCallInvoker_(jsCallInvoker), nativeCallInvoker_(nativeCallInvoker), delegate_(jni::make_global(delegate)), - turboModuleCache_(std::make_shared()) {} + turboModuleCache_(std::make_shared()) { + if (useGlobalCallbackCleanupScopeUsingRetainJSCallback) { + longLivedObjectCollection_ = nullptr; + retainJSCallback_ = [](jsi::Function &&callback, + jsi::Runtime &runtime, + std::shared_ptr jsInvoker) { + return CallbackWrapper::createWeak( + std::move(callback), runtime, jsInvoker); + }; + } else if (useTurboModuleManagerCallbackCleanupScope) { + longLivedObjectCollection_ = std::make_shared(); + retainJSCallback_ = [longLivedObjectCollection = + longLivedObjectCollection_]( + jsi::Function &&callback, + jsi::Runtime &runtime, + std::shared_ptr jsInvoker) { + return CallbackWrapper::createWeak( + longLivedObjectCollection, std::move(callback), runtime, jsInvoker); + }; + } +} jni::local_ref TurboModuleManager::initHybrid( jni::alias_ref jThis, - jlong jsContext, + jni::alias_ref runtimeExecutor, jni::alias_ref jsCallInvokerHolder, jni::alias_ref nativeCallInvokerHolder, jni::alias_ref delegate, - bool enablePromiseAsyncDispatch) { + bool useGlobalCallbackCleanupScopeUsingRetainJSCallback, + bool useTurboModuleManagerCallbackCleanupScope) { auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); auto nativeCallInvoker = nativeCallInvokerHolder->cthis()->getCallInvoker(); - JavaTurboModule::enablePromiseAsyncDispatch(enablePromiseAsyncDispatch); - return makeCxxInstance( jThis, - (jsi::Runtime *)jsContext, + runtimeExecutor->cthis()->get(), jsCallInvoker, nativeCallInvoker, - delegate); + delegate, + useGlobalCallbackCleanupScopeUsingRetainJSCallback, + useTurboModuleManagerCallbackCleanupScope); } void TurboModuleManager::registerNatives() { @@ -63,91 +86,97 @@ void TurboModuleManager::registerNatives() { } void TurboModuleManager::installJSIBindings() { - if (!runtime_ || !jsCallInvoker_) { + if (!jsCallInvoker_) { return; // Runtime doesn't exist when attached to Chrome debugger. } - auto turboModuleProvider = - [turboModuleCache_ = std::weak_ptr(turboModuleCache_), - jsCallInvoker_ = std::weak_ptr(jsCallInvoker_), - nativeCallInvoker_ = std::weak_ptr(nativeCallInvoker_), - delegate_ = jni::make_weak(delegate_), - javaPart_ = jni::make_weak(javaPart_)]( - const std::string &name, - const jsi::Value *schema) -> std::shared_ptr { - auto turboModuleCache = turboModuleCache_.lock(); - auto jsCallInvoker = jsCallInvoker_.lock(); - auto nativeCallInvoker = nativeCallInvoker_.lock(); - auto delegate = delegate_.lockLocal(); - auto javaPart = javaPart_.lockLocal(); - - if (!turboModuleCache || !jsCallInvoker || !nativeCallInvoker || - !delegate || !javaPart) { - return nullptr; - } - - const char *moduleName = name.c_str(); - - TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName); + runtimeExecutor_([this](jsi::Runtime &runtime) { + auto turboModuleProvider = + [turboModuleCache_ = std::weak_ptr(turboModuleCache_), + jsCallInvoker_ = std::weak_ptr(jsCallInvoker_), + nativeCallInvoker_ = std::weak_ptr(nativeCallInvoker_), + delegate_ = jni::make_weak(delegate_), + javaPart_ = jni::make_weak(javaPart_), + retainJSCallback = retainJSCallback_]( + const std::string &name) -> std::shared_ptr { + auto turboModuleCache = turboModuleCache_.lock(); + auto jsCallInvoker = jsCallInvoker_.lock(); + auto nativeCallInvoker = nativeCallInvoker_.lock(); + auto delegate = delegate_.lockLocal(); + auto javaPart = javaPart_.lockLocal(); + + if (!turboModuleCache || !jsCallInvoker || !nativeCallInvoker || + !delegate || !javaPart) { + return nullptr; + } + + const char *moduleName = name.c_str(); + + TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName); + + auto turboModuleLookup = turboModuleCache->find(name); + if (turboModuleLookup != turboModuleCache->end()) { + TurboModulePerfLogger::moduleJSRequireBeginningCacheHit(moduleName); + TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName); + return turboModuleLookup->second; + } - auto turboModuleLookup = turboModuleCache->find(name); - if (turboModuleLookup != turboModuleCache->end()) { - TurboModulePerfLogger::moduleJSRequireBeginningCacheHit(moduleName); TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName); - return turboModuleLookup->second; - } - TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName); + auto cxxModule = delegate->cthis()->getTurboModule(name, jsCallInvoker); + if (cxxModule) { + turboModuleCache->insert({name, cxxModule}); + return cxxModule; + } + + static auto getLegacyCxxModule = + javaPart->getClass() + ->getMethod( + const std::string &)>("getLegacyCxxModule"); + auto legacyCxxModule = getLegacyCxxModule(javaPart.get(), name); + + if (legacyCxxModule) { + TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName); + + auto turboModule = std::make_shared( + legacyCxxModule->cthis()->getModule(), jsCallInvoker); + turboModuleCache->insert({name, turboModule}); + + TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName); + return turboModule; + } + + static auto getJavaModule = + javaPart->getClass() + ->getMethod(const std::string &)>( + "getJavaModule"); + auto moduleInstance = getJavaModule(javaPart.get(), name); + + if (moduleInstance) { + TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName); + JavaTurboModule::InitParams params = { + .moduleName = name, + .instance = moduleInstance, + .jsInvoker = jsCallInvoker, + .nativeInvoker = nativeCallInvoker, + .retainJSCallback = retainJSCallback}; + + auto turboModule = delegate->cthis()->getTurboModule(name, params); + turboModuleCache->insert({name, turboModule}); + TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName); + return turboModule; + } - auto cxxModule = delegate->cthis()->getTurboModule(name, jsCallInvoker); - if (cxxModule) { - turboModuleCache->insert({name, cxxModule}); - return cxxModule; - } - - static auto getLegacyCxxModule = - javaPart->getClass() - ->getMethod( - const std::string &)>("getLegacyCxxModule"); - auto legacyCxxModule = getLegacyCxxModule(javaPart.get(), name); - - if (legacyCxxModule) { - TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName); - - auto turboModule = std::make_shared( - legacyCxxModule->cthis()->getModule(), jsCallInvoker); - turboModuleCache->insert({name, turboModule}); - - TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName); - return turboModule; - } + return nullptr; + }; - static auto getJavaModule = - javaPart->getClass() - ->getMethod(const std::string &)>( - "getJavaModule"); - auto moduleInstance = getJavaModule(javaPart.get(), name); - - if (moduleInstance) { - TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName); - JavaTurboModule::InitParams params = {.moduleName = name, - .instance = moduleInstance, - .jsInvoker = jsCallInvoker, - .nativeInvoker = nativeCallInvoker}; - - auto turboModule = delegate->cthis()->getTurboModule(name, params); - turboModuleCache->insert({name, turboModule}); - TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName); - return turboModule; + if (longLivedObjectCollection_) { + TurboModuleBinding::install( + runtime, std::move(turboModuleProvider), longLivedObjectCollection_); + } else { + TurboModuleBinding::install(runtime, std::move(turboModuleProvider)); } - - return nullptr; - }; - - jsCallInvoker_->invokeAsync( - [this, turboModuleProvider = std::move(turboModuleProvider)]() -> void { - TurboModuleBinding::install(*runtime_, std::move(turboModuleProvider)); - }); + }); } } // namespace react diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.h b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.h index 899d2a1d1cf8e..6b8fd0669b861 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.h +++ b/android/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.h @@ -9,12 +9,15 @@ #include #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -27,21 +30,25 @@ class TurboModuleManager : public jni::HybridClass { "Lcom/facebook/react/turbomodule/core/TurboModuleManager;"; static jni::local_ref initHybrid( jni::alias_ref jThis, - jlong jsContext, + jni::alias_ref runtimeExecutor, jni::alias_ref jsCallInvokerHolder, jni::alias_ref nativeCallInvokerHolder, jni::alias_ref delegate, - bool enablePromiseAsyncDispatch); + bool useGlobalCallbackCleanupScopeUsingRetainJSCallback, + bool useTurboModuleManagerCallbackCleanupScope); static void registerNatives(); private: friend HybridBase; jni::global_ref javaPart_; - jsi::Runtime *runtime_; + RuntimeExecutor runtimeExecutor_; std::shared_ptr jsCallInvoker_; std::shared_ptr nativeCallInvoker_; jni::global_ref delegate_; + JSCallbackRetainer retainJSCallback_; + std::shared_ptr longLivedObjectCollection_; + using TurboModuleCache = std::unordered_map>; @@ -56,10 +63,12 @@ class TurboModuleManager : public jni::HybridClass { void installJSIBindings(); explicit TurboModuleManager( jni::alias_ref jThis, - jsi::Runtime *rt, + RuntimeExecutor runtimeExecutor, std::shared_ptr jsCallInvoker, std::shared_ptr nativeCallInvoker, - jni::alias_ref delegate); + jni::alias_ref delegate, + bool useGlobalCallbackCleanupScopeUsingRetainJSCallback, + bool useTurboModuleManagerCallbackCleanupScope); }; } // namespace react diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK index 985aeb4e9ed37..9979f6dfdde5d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK @@ -13,6 +13,7 @@ rn_android_library( "DisplayMetricsHolder.java", ], ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -31,11 +32,14 @@ rn_android_library( ":DisplayMetrics", react_native_dep("java/com/facebook/systrace:systrace"), react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), + react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"), react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_target("java/com/facebook/debug/tags:tags"), react_native_target("java/com/facebook/debug/holder:holder"), react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/uimanager/jni:jni"), react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/common/mapbuffer:mapbuffer"), react_native_target("java/com/facebook/react/config:config"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/modules/core:core"), @@ -43,7 +47,6 @@ rn_android_library( react_native_target("java/com/facebook/react/touch:touch"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager/util:util"), - react_native_target("java/com/facebook/react/uimanager/common:common"), react_native_target("res:uimanager"), ], exported_deps = [ @@ -51,6 +54,7 @@ rn_android_library( react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/uimanager/common:common"), + react_native_target("java/com/facebook/react/uimanager/interfaces:interfaces"), ], ) @@ -59,6 +63,7 @@ rn_android_library( srcs = [ "DisplayMetricsHolder.java", ], + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index 2e486476d97ac..15a5ad23a6222 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -166,8 +166,22 @@ public void setViewState(@NonNull T view, @Nullable ReadableMap accessibilitySta if (accessibilityState == null) { return; } + if (accessibilityState.hasKey("selected")) { + boolean prevSelected = view.isSelected(); + boolean nextSelected = accessibilityState.getBoolean("selected"); + view.setSelected(nextSelected); + + // For some reason, Android does not announce "unselected" when state changes. + // This is inconsistent with other platforms, but also with the "checked" state. + // So manually announce this. + if (view.isAccessibilityFocused() && prevSelected && !nextSelected) { + view.announceForAccessibility( + view.getContext().getString(R.string.reactandroid_state_unselected_description)); + } + } else { + view.setSelected(false); + } view.setTag(R.id.reactandroid_accessibility_state, accessibilityState); - view.setSelected(false); view.setEnabled(true); // For states which don't have corresponding methods in diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolver.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolver.java new file mode 100644 index 0000000000000..002477dbdbbc5 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolver.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager; + +import com.facebook.proguard.annotations.DoNotStripAny; + +@DoNotStripAny +public interface ComponentNameResolver { + + /* returns a list of all the component names that are registered in React Native. */ + String[] getComponentNames(); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolverManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolverManager.java new file mode 100644 index 0000000000000..9f519114ed705 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ComponentNameResolverManager.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.proguard.annotations.DoNotStripAny; +import com.facebook.react.bridge.RuntimeExecutor; +import com.facebook.soloader.SoLoader; + +@DoNotStripAny +public class ComponentNameResolverManager { + + static { + staticInit(); + } + + @DoNotStrip + @SuppressWarnings("unused") + private final HybridData mHybridData; + + public ComponentNameResolverManager( + RuntimeExecutor runtimeExecutor, Object componentNameResolver) { + mHybridData = initHybrid(runtimeExecutor, componentNameResolver); + installJSIBindings(); + } + + private native HybridData initHybrid( + RuntimeExecutor runtimeExecutor, Object componentNameResolver); + + private native void installJSIBindings(); + + private static void staticInit() { + SoLoader.loadLibrary("uimanagerjni"); + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.java index 8ecd9cdf94221..3caf9bcf44cb4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.java @@ -13,9 +13,8 @@ import android.view.WindowManager; import androidx.annotation.Nullable; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; -import java.util.HashMap; -import java.util.Map; /** * Holds an instance of the current DisplayMetrics so we don't have to thread it through all the @@ -81,40 +80,20 @@ public static DisplayMetrics getScreenDisplayMetrics() { return sScreenDisplayMetrics; } - public static Map> getDisplayMetricsMap(double fontScale) { + public static WritableMap getDisplayMetricsWritableMap(double fontScale) { Assertions.assertCondition( sWindowDisplayMetrics != null && sScreenDisplayMetrics != null, - "DisplayMetricsHolder must be initialized with initDisplayMetricsIfNotInitialized or initDisplayMetrics"); - final Map> result = new HashMap<>(); - result.put("windowPhysicalPixels", getPhysicalPixelsMap(sWindowDisplayMetrics, fontScale)); - result.put("screenPhysicalPixels", getPhysicalPixelsMap(sScreenDisplayMetrics, fontScale)); - return result; - } - - public static WritableNativeMap getDisplayMetricsNativeMap(double fontScale) { - Assertions.assertCondition( - sWindowDisplayMetrics != null && sScreenDisplayMetrics != null, - "DisplayMetricsHolder must be initialized with initDisplayMetricsIfNotInitialized or initDisplayMetrics"); + "DisplayMetricsHolder must be initialized with initDisplayMetricsIfNotInitialized or" + + " initDisplayMetrics"); final WritableNativeMap result = new WritableNativeMap(); result.putMap( - "windowPhysicalPixels", getPhysicalPixelsNativeMap(sWindowDisplayMetrics, fontScale)); + "windowPhysicalPixels", getPhysicalPixelsWritableMap(sWindowDisplayMetrics, fontScale)); result.putMap( - "screenPhysicalPixels", getPhysicalPixelsNativeMap(sScreenDisplayMetrics, fontScale)); - return result; - } - - private static Map getPhysicalPixelsMap( - DisplayMetrics displayMetrics, double fontScale) { - final Map result = new HashMap<>(); - result.put("width", displayMetrics.widthPixels); - result.put("height", displayMetrics.heightPixels); - result.put("scale", displayMetrics.density); - result.put("fontScale", fontScale); - result.put("densityDpi", displayMetrics.densityDpi); + "screenPhysicalPixels", getPhysicalPixelsWritableMap(sScreenDisplayMetrics, fontScale)); return result; } - private static WritableNativeMap getPhysicalPixelsNativeMap( + private static WritableMap getPhysicalPixelsWritableMap( DisplayMetrics displayMetrics, double fontScale) { final WritableNativeMap result = new WritableNativeMap(); result.putInt("width", displayMetrics.widthPixels); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java index 073b91122f033..1a1cdd0764222 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricViewStateManager.java @@ -11,7 +11,6 @@ import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; -import com.facebook.react.config.ReactFeatureFlags; /** * This is a helper base class for ViewGroups that use Fabric State. @@ -67,33 +66,20 @@ private void setState( return; } - Runnable failureRunnable = null; - if (ReactFeatureFlags.enableExperimentalStateUpdateRetry) { - failureRunnable = - new Runnable() { - @Override - // Run on the UI thread - public void run() { - FLog.e(TAG, "UpdateState failed - retrying! " + numTries); - setState(stateWrapper, stateUpdateCallback, numTries + 1); - } - }; - } @Nullable WritableMap stateUpdate = stateUpdateCallback.getStateUpdate(); if (stateUpdate == null) { return; } - stateWrapper.updateState( - stateUpdate, - // Failure callback - this is run if the updateState call fails - failureRunnable); + + // TODO: State update cannot fail; remove `failureRunnable` and custom retrying logic. + stateWrapper.updateState(stateUpdate); } public void setState(final StateUpdateCallback stateUpdateCallback) { setState(mStateWrapper, stateUpdateCallback, 0); } - public @Nullable ReadableMap getState() { - return mStateWrapper != null ? mStateWrapper.getState() : null; + public @Nullable ReadableMap getStateData() { + return mStateWrapper != null ? mStateWrapper.getStateData() : null; } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java index e103cd3923c8e..901961494ccb6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java @@ -12,6 +12,7 @@ import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.common.ReactConstants; +import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.TouchEvent; import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; @@ -50,6 +51,25 @@ public void onChildStartedNativeGesture( mTargetTag = -1; } + /** + * See Event.java. By contract, this surfaceId should be a valid SurfaceId in Fabric, and should + * ALWAYS return -1 in non-Fabric. + * + * @return + */ + private int getSurfaceId() { + if (mRootViewGroup != null + && mRootViewGroup instanceof ReactRoot + && ((ReactRoot) mRootViewGroup).getUIManagerType() == UIManagerType.FABRIC) { + if (mRootViewGroup.getContext() instanceof ThemedReactContext) { + ThemedReactContext context = (ThemedReactContext) mRootViewGroup.getContext(); + return context.getSurfaceId(); + } + return ((ReactRoot) mRootViewGroup).getRootViewTag(); + } + return -1; + } + /** * Main catalyst view is responsible for collecting and sending touch events to JS. This method * reacts for an incoming android native touch events ({@link MotionEvent}) and calls into {@link @@ -70,9 +90,11 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { // this gesture mChildIsHandlingNativeGesture = false; mGestureStartTime = ev.getEventTime(); + mTargetTag = findTargetTagAndSetCoordinates(ev); eventDispatcher.dispatchEvent( TouchEvent.obtain( + getSurfaceId(), mTargetTag, TouchEventType.START, ev, @@ -97,6 +119,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { findTargetTagAndSetCoordinates(ev); eventDispatcher.dispatchEvent( TouchEvent.obtain( + getSurfaceId(), mTargetTag, TouchEventType.END, ev, @@ -111,6 +134,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { findTargetTagAndSetCoordinates(ev); eventDispatcher.dispatchEvent( TouchEvent.obtain( + getSurfaceId(), mTargetTag, TouchEventType.MOVE, ev, @@ -122,6 +146,7 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { // New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer eventDispatcher.dispatchEvent( TouchEvent.obtain( + getSurfaceId(), mTargetTag, TouchEventType.START, ev, @@ -130,9 +155,10 @@ public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) { mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); } else if (action == MotionEvent.ACTION_POINTER_UP) { - // Exactly onw of the pointers goes up + // Exactly one of the pointers goes up eventDispatcher.dispatchEvent( TouchEvent.obtain( + getSurfaceId(), mTargetTag, TouchEventType.END, ev, @@ -181,6 +207,7 @@ private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher event Assertions.assertNotNull(eventDispatcher) .dispatchEvent( TouchEvent.obtain( + getSurfaceId(), mTargetTag, TouchEventType.CANCEL, androidEvent, diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index 000832979d2a2..c54573eca026b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -29,7 +29,6 @@ import com.facebook.react.bridge.SoftAssertions; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.build.ReactBuildConfig; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.touch.JSResponderHandler; import com.facebook.react.uimanager.layoutanimation.LayoutAnimationController; import com.facebook.react.uimanager.layoutanimation.LayoutAnimationListener; @@ -257,16 +256,9 @@ public synchronized void createView(ThemedReactContext themedContext, int tag, S SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT_VIEW, "NativeViewHierarchyManager_createView").arg("tag", tag).arg("className", className).flush(); try { ViewManager viewManager = mViewManagers.get(className); - View view = viewManager.createView(themedContext, null, null, mJSResponderHandler); + View view = viewManager.createView(tag, themedContext, initialProps, null, mJSResponderHandler); mTagsToViews.put(tag, view); mTagsToViewManagers.put(tag, viewManager); - // Use android View id field to store React tag. This is possible since we don't inflate - // React views from layout xmls. Thus it is easier to just reuse that field instead of - // creating another (potentially much more expensive) mapping from view to React tag - view.setId(tag); - if (initialProps != null) { - viewManager.updateProperties(view, initialProps); - } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_VIEW); } @@ -689,8 +681,8 @@ public synchronized void dispatchCommand(int reactTag, String commandId, @Nullab throw new RetryableMountingLayerException("Trying to send command to a non-existing view with tag [" + reactTag + "] and command " + commandId); } ViewManager viewManager = resolveViewManager(reactTag); - ViewManagerDelegate delegate; - if (ReactFeatureFlags.useViewManagerDelegatesForCommands && (delegate = viewManager.getDelegate()) != null) { + ViewManagerDelegate delegate = viewManager.getDelegate(); + if (delegate != null) { delegate.receiveCommand(view, commandId, args); } else { viewManager.receiveCommand(view, commandId, args); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java index 3e196698f24e9..66e1cb38f3a92 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java @@ -13,7 +13,6 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMapKeySetIterator; -import com.facebook.react.config.ReactFeatureFlags; /** * Class responsible for optimizing the native view hierarchy while still respecting the final UI @@ -425,18 +424,16 @@ private void transitionLayoutOnlyViewToNativeView( // Bit of a hack: we need to update the layout of this node's children now that it's no longer // layout-only, but we may still receive more layout updates at the end of this batch that we // don't want to ignore. - if (ReactFeatureFlags.enableTransitionLayoutOnlyViewCleanup) { - FLog.i( - TAG, - "Transitioning LayoutOnlyView - tag: " - + node.getReactTag() - + " - rootTag: " - + node.getRootTag() - + " - hasProps: " - + (props != null) - + " - tagsWithLayout.size: " - + mTagsWithLayoutVisited.size()); - } + FLog.i( + TAG, + "Transitioning LayoutOnlyView - tag: " + + node.getReactTag() + + " - rootTag: " + + node.getRootTag() + + " - hasProps: " + + (props != null) + + " - tagsWithLayout.size: " + + mTagsWithLayoutVisited.size()); Assertions.assertCondition(mTagsWithLayoutVisited.size() == 0); applyLayoutBase(node); for (int i = 0; i < node.getChildCount(); i++) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java index 1278dc253d00b..d7eb8de6860c2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java @@ -7,11 +7,11 @@ package com.facebook.react.uimanager; +import androidx.annotation.Nullable; import androidx.core.util.Pools; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event used to notify JS component about changes of its position or dimensions */ public class OnLayoutEvent extends Event { @@ -21,12 +21,18 @@ public class OnLayoutEvent extends Event { private int mX, mY, mWidth, mHeight; + @Deprecated public static OnLayoutEvent obtain(int viewTag, int x, int y, int width, int height) { + return obtain(-1, viewTag, x, y, width, height); + } + + public static OnLayoutEvent obtain( + int surfaceId, int viewTag, int x, int y, int width, int height) { OnLayoutEvent event = EVENTS_POOL.acquire(); if (event == null) { event = new OnLayoutEvent(); } - event.init(viewTag, x, y, width, height); + event.init(surfaceId, viewTag, x, y, width, height); return event; } @@ -37,8 +43,13 @@ public void onDispose() { private OnLayoutEvent() {} + @Deprecated protected void init(int viewTag, int x, int y, int width, int height) { - super.init(viewTag); + init(-1, viewTag, x, y, width, height); + } + + protected void init(int surfaceId, int viewTag, int x, int y, int width, int height) { + super.init(surfaceId, viewTag); mX = x; mY = y; mWidth = width; @@ -50,8 +61,9 @@ public String getEventName() { return "topLayout"; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { + protected WritableMap getEventData() { WritableMap layout = Arguments.createMap(); layout.putDouble("x", PixelUtil.toDIPFromPixel(mX)); layout.putDouble("y", PixelUtil.toDIPFromPixel(mY)); @@ -61,7 +73,6 @@ public void dispatch(RCTEventEmitter rctEventEmitter) { WritableMap event = Arguments.createMap(); event.putMap("layout", layout); event.putInt("target", getViewTag()); - - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event); + return event; } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.java index 8400527f6fa14..e4d9c7ee2243a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.java @@ -24,11 +24,6 @@ public static float toPixelFromDIP(double value) { return toPixelFromDIP((float) value); } - /** Convert from PX to SP */ - public static float toSPFromPixel(float value) { - return value / DisplayMetricsHolder.getScreenDisplayMetrics().scaledDensity; - } - /** Convert from SP to PX */ public static float toPixelFromSP(float value) { return toPixelFromSP(value, Float.NaN); @@ -58,6 +53,6 @@ public static float toDIPFromPixel(float value) { /** @return {@link float} that represents the density of the display metrics for device screen. */ public static float getDisplayMetricDensity() { - return DisplayMetricsHolder.getScreenDisplayMetrics().density; + return DisplayMetricsHolder.getWindowDisplayMetrics().density; } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java index 813dcd45fb1fb..4b10c581ba490 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java @@ -26,7 +26,7 @@ import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMapKeySetIterator; @@ -35,7 +35,6 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.uimanager.events.RCTEventEmitter; import java.util.HashMap; /** @@ -83,6 +82,7 @@ private void scheduleAccessibilityEventSender(View host) { public enum AccessibilityRole { NONE, BUTTON, + TOGGLEBUTTON, LINK, SEARCH, IMAGE, @@ -107,12 +107,15 @@ public enum AccessibilityRole { TAB, TABLIST, TIMER, + LIST, TOOLBAR; public static String getValue(AccessibilityRole role) { switch (role) { case BUTTON: return "android.widget.Button"; + case TOGGLEBUTTON: + return "android.widget.ToggleButton"; case SEARCH: return "android.widget.EditText"; case IMAGE: @@ -133,6 +136,8 @@ public static String getValue(AccessibilityRole role) { return "android.widget.SpinButton"; case SWITCH: return "android.widget.Switch"; + case LIST: + return "android.widget.AbsListView"; case NONE: case LINK: case SUMMARY: @@ -293,27 +298,28 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) { final WritableMap event = Arguments.createMap(); event.putString("actionName", mAccessibilityActionsMap.get(action)); ReactContext reactContext = (ReactContext) host.getContext(); - if (reactContext.hasActiveCatalystInstance()) { + if (reactContext.hasActiveReactInstance()) { final int reactTag = host.getId(); + final int surfaceId = UIManagerHelper.getSurfaceId(reactContext); UIManager uiManager = UIManagerHelper.getUIManager(reactContext, reactTag); if (uiManager != null) { uiManager .getEventDispatcher() .dispatchEvent( - new Event(reactTag) { + new Event(surfaceId, reactTag) { @Override public String getEventName() { return TOP_ACCESSIBILITY_ACTION_EVENT; } @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(reactTag, TOP_ACCESSIBILITY_ACTION_EVENT, event); + protected WritableMap getEventData() { + return event; } }); } } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new ReactNoCrashSoftException("Cannot get RCTEventEmitter, no CatalystInstance")); } @@ -382,16 +388,16 @@ public static void setRole( spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0); nodeInfo.setText(spannable); } - } else if (role.equals(AccessibilityRole.SEARCH)) { - nodeInfo.setRoleDescription(context.getString(R.string.reactandroid_search_description)); } else if (role.equals(AccessibilityRole.IMAGE)) { nodeInfo.setRoleDescription(context.getString(R.string.reactandroid_image_description)); } else if (role.equals(AccessibilityRole.IMAGEBUTTON)) { nodeInfo.setRoleDescription(context.getString(R.string.reactandroid_imagebutton_description)); nodeInfo.setClickable(true); } else if (role.equals(AccessibilityRole.BUTTON)) { - nodeInfo.setRoleDescription(context.getString(R.string.reactandroid_button_description)); nodeInfo.setClickable(true); + } else if (role.equals(AccessibilityRole.TOGGLEBUTTON)) { + nodeInfo.setClickable(true); + nodeInfo.setCheckable(true); } else if (role.equals(AccessibilityRole.SUMMARY)) { nodeInfo.setRoleDescription(context.getString(R.string.reactandroid_summary_description)); } else if (role.equals(AccessibilityRole.HEADER)) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingProhibitedView.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingProhibitedView.java new file mode 100644 index 0000000000000..c6ff728e0a467 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingProhibitedView.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager; + +/** + * Some Views may not function if added directly to a ViewGroup that clips them. For example, TTRC + * markers may rely on `onDraw` functionality to work properly, and will break if they're clipped + * out of the View hierarchy for any resaon. + * + *

This situation can occur more often in Fabric with View Flattening. We may prevent this sort + * of View Flattening from occurring in the future, but the connection is not entirely certain. + * + *

This can occur either because ReactViewGroup clips them out, using the ordinarary subview + * clipping feature. It is also possible if a View is added directly to a ReactRootView below the + * fold of the screen. + * + *

Generally the solution is to prevent View flattening in JS by adding `collapsable=false` to a + * parent component of the clipped view, and/or move the View higher up in the hierarchy so it is + * always rendered within the first page of the screen. + */ +public interface ReactClippingProhibitedView {} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingViewGroupHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingViewGroupHelper.java index 077fcd050860c..8c9399073af3d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingViewGroupHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactClippingViewGroupHelper.java @@ -8,7 +8,6 @@ package com.facebook.react.uimanager; import android.graphics.Rect; -import android.graphics.RectF; import android.view.View; import android.view.ViewParent; import javax.annotation.concurrent.NotThreadSafe; @@ -44,12 +43,16 @@ public static void calculateClippingRect(View view, Rect outputRect) { // Intersect the view with the parent's rectangle // This will result in the overlap with coordinates in the parent space if (!sHelperRect.intersect( - view.getLeft(), view.getTop(), view.getRight(), view.getBottom())) { + view.getLeft(), + view.getTop() + (int) view.getTranslationY(), + view.getRight(), + view.getBottom() + (int) view.getTranslationY())) { outputRect.setEmpty(); return; } // Now we move the coordinates to the View's coordinate space sHelperRect.offset(-view.getLeft(), -view.getTop()); + sHelperRect.offset(-(int) view.getTranslationX(), -(int) view.getTranslationY()); sHelperRect.offset(view.getScrollX(), view.getScrollY()); outputRect.set(sHelperRect); return; @@ -57,51 +60,4 @@ public static void calculateClippingRect(View view, Rect outputRect) { } view.getDrawingRect(outputRect); } - - public static boolean getChildVisibleRectHelper( - View child, Rect r, android.graphics.Point offset, View parent, String overflow) { - // This is based on the Android ViewGroup implementation, modified to clip child rects - // if overflow is set to ViewProps.HIDDEN. This effectively solves Issue #23870 which - // appears to have been introduced by FLAG_CLIP_CHILDREN being forced false - // regardless of whether clipping is desired. - final RectF rect = new RectF(); - rect.set(r); - - child.getMatrix().mapRect(rect); - - final int dx = child.getLeft() - parent.getScrollX(); - final int dy = child.getTop() - parent.getScrollY(); - - rect.offset(dx, dy); - - if (offset != null) { - float[] position = new float[2]; - position[0] = offset.x; - position[1] = offset.y; - child.getMatrix().mapPoints(position); - offset.x = Math.round(position[0]) + dx; - offset.y = Math.round(position[1]) + dy; - } - - final int width = parent.getRight() - parent.getLeft(); - final int height = parent.getBottom() - parent.getTop(); - - boolean rectIsVisible = true; - - ViewParent grandparent = parent.getParent(); - if (grandparent == null || ViewProps.HIDDEN.equals(overflow)) { - rectIsVisible = rect.intersect(0, 0, width, height); - } - - r.set( - (int) Math.floor(rect.left), - (int) Math.floor(rect.top), - (int) Math.ceil(rect.right), - (int) Math.ceil(rect.bottom)); - - if (rectIsVisible && grandparent != null) { - rectIsVisible = grandparent.getChildVisibleRect(parent, r, offset); - } - return rectIsVisible; - } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactOverflowView.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactOverflowView.java new file mode 100644 index 0000000000000..89c437e386966 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactOverflowView.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager; + +import android.view.View; +import androidx.annotation.Nullable; + +/** + * Interface that should be implemented by {@link View} subclasses that support {@code overflow} + * style. This allows the overflow information to be used by {@link TouchTargetHelper} to determine + * if a View is touchable. + */ +public interface ReactOverflowView { + /** + * Gets the overflow state of a view. If set, this should be one of {@link ViewProps#HIDDEN}, + * {@link ViewProps#VISIBLE} or {@link ViewProps#SCROLL}. + */ + @Nullable + String getOverflow(); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactRoot.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactRoot.java index 2fba8c52164c7..9ce17ea680954 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactRoot.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactRoot.java @@ -11,10 +11,16 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; import com.facebook.react.uimanager.common.UIManagerType; +import java.util.concurrent.atomic.AtomicInteger; /** Interface for the root native view of a React native application */ public interface ReactRoot { + /** This constant represents that ReactRoot hasn't started yet or it has been destroyed. */ + int STATE_STOPPED = 0; + /** This constant represents that ReactRoot has started. */ + int STATE_STARTED = 1; + /** Return cached launch properties for app */ @Nullable Bundle getAppProperties(); @@ -58,4 +64,12 @@ public interface ReactRoot { @Deprecated @Nullable String getSurfaceID(); + + /** + * This API is likely to change once the fix of T78832286 is confirmed TODO: T78832286 revisit + * this API + * + * @return an {@link AtomicInteger} that represents the state of the ReactRoot object. WARNING: + */ + AtomicInteger getState(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/RootViewUtil.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/RootViewUtil.java index 41bae5be249ae..2c2e35460b807 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/RootViewUtil.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/RootViewUtil.java @@ -7,8 +7,11 @@ package com.facebook.react.uimanager; +import android.graphics.Point; +import android.graphics.Rect; import android.view.View; import android.view.ViewParent; +import androidx.annotation.UiThread; import com.facebook.infer.annotation.Assertions; public class RootViewUtil { @@ -28,4 +31,19 @@ public static RootView getRootView(View reactView) { current = (View) next; } } + + @UiThread + public static Point getViewportOffset(View v) { + int[] locationInWindow = new int[2]; + v.getLocationInWindow(locationInWindow); + + // we need to subtract visibleWindowCoords - to subtract possible window insets, split + // screen or multi window + Rect visibleWindowFrame = new Rect(); + v.getWindowVisibleDisplayFrame(visibleWindowFrame); + locationInWindow[0] -= visibleWindowFrame.left; + locationInWindow[1] -= visibleWindowFrame.top; + + return new Point(locationInWindow[0], locationInWindow[1]); + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/StateWrapper.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/StateWrapper.java index 51bbc14349a44..8bec238604f64 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/StateWrapper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/StateWrapper.java @@ -9,6 +9,8 @@ import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import javax.annotation.Nullable; /** * This is a wrapper that can be used for passing State objects from Fabric C++ core to @@ -16,14 +18,32 @@ * by calling updateState, which communicates state back to the C++ layer. */ public interface StateWrapper { + + /** + * Get a ReadableMapBuffer object from the C++ layer, which is a K/V map of short keys to values. + * + *

Unstable API - DO NOT USE. + */ + @Nullable + ReadableMapBuffer getStatDataMapBuffer(); + /** * Get a ReadableNativeMap object from the C++ layer, which is a K/V map of string keys to values. */ - ReadableNativeMap getState(); + @Nullable + ReadableNativeMap getStateData(); + + /** + * Pass a map of values back to the C++ layer. The operation is performed synchronously and cannot + * fail. + */ + void updateState(WritableMap map); /** - * Pass a map of values back to the C++ layer. /Last/ runnable passed into updateState is called - * if an updateState call fails. + * Mark state as unused and clean up in Java and in native. This should be called as early as + * possible when you know a StateWrapper will no longer be used. If there's ANY chance of it being + * used legitimately, don't destroy it! It is expected that all StateWrappers are destroyed + * immediately upon stopSurface. */ - void updateState(WritableMap map, Runnable failureCallback); + void destroyState(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java index 834208a962e67..15701d63618a4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java @@ -27,20 +27,32 @@ public class ThemedReactContext extends ReactContext { private final ReactApplicationContext mReactApplicationContext; - @Nullable private final String mSurfaceID; + @Nullable private final String mModuleName; + private final int mSurfaceId; + @Deprecated public ThemedReactContext(ReactApplicationContext reactApplicationContext, Context base) { - this(reactApplicationContext, base, null); + this(reactApplicationContext, base, null, -1); } + @Deprecated public ThemedReactContext( - ReactApplicationContext reactApplicationContext, Context base, @Nullable String surfaceID) { + ReactApplicationContext reactApplicationContext, Context base, @Nullable String moduleName) { + this(reactApplicationContext, base, moduleName, -1); + } + + public ThemedReactContext( + ReactApplicationContext reactApplicationContext, + Context base, + @Nullable String moduleName, + int surfaceId) { super(base); if (reactApplicationContext.hasCatalystInstance()) { initializeWithInstance(reactApplicationContext.getCatalystInstance()); } mReactApplicationContext = reactApplicationContext; - mSurfaceID = surfaceID; + mModuleName = moduleName; + mSurfaceId = surfaceId; } @Override @@ -64,11 +76,27 @@ public boolean hasCurrentActivity() { } /** - * @return a {@link String} that represents the ID of the js application that is being rendered - * with this {@link ThemedReactContext} + * This is misnamed but has some uses out in the wild. It will be deleted in a future release of + * RN. + * + * @return a {@link String} that represents the module name of the js application that is being + * rendered with this {@link ThemedReactContext} */ + @Deprecated public @Nullable String getSurfaceID() { - return mSurfaceID; + return mModuleName; + } + + /** + * @return a {@link String} that represents the module name of the js application that is being + * rendered with this {@link ThemedReactContext} + */ + public @Nullable String getModuleName() { + return mModuleName; + } + + public int getSurfaceId() { + return mSurfaceId; } public ReactApplicationContext getReactApplicationContext() { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java index fbcda93d07cae..05d2b48971b43 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java @@ -17,6 +17,9 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.touch.ReactHitSlopView; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; /** * Class responsible for identifying which react view should handle a given {@link MotionEvent}. It @@ -80,7 +83,7 @@ public static int findTargetTagAndCoordinatesForTouch( // Store eventCoords in array so that they are modified to be relative to the targetView found. viewCoords[0] = eventX; viewCoords[1] = eventY; - View nativeTargetView = findTouchTargetView(viewCoords, viewGroup); + View nativeTargetView = findTouchTargetViewWithPointerEvents(viewCoords, viewGroup, null); if (nativeTargetView != null) { View reactTargetView = findClosestReactAncestor(nativeTargetView); if (reactTargetView != null) { @@ -93,6 +96,39 @@ public static int findTargetTagAndCoordinatesForTouch( return targetTag; } + /** + * Find touch event target view within the provided container given the coordinates provided via + * {@link MotionEvent}. + * + * @param eventX the X screen coordinate of the touch location + * @param eventY the Y screen coordinate of the touch location + * @param viewGroup the container view to traverse + * @param viewCoords an out parameter that will return the X,Y value in the target view + * @return If a target was found, returns a path through the view tree of all react tags that are + * a container for the touch target, ordered from target to root (last element) + */ + public static List findTargetPathAndCoordinatesForTouch( + float eventX, float eventY, ViewGroup viewGroup, float[] viewCoords) { + UiThreadUtil.assertOnUiThread(); + + // Store eventCoords in array so that they are modified to be relative to the targetView found. + viewCoords[0] = eventX; + viewCoords[1] = eventY; + + List pathAccumulator = new ArrayList<>(); + View targetView = findTouchTargetViewWithPointerEvents(viewCoords, viewGroup, pathAccumulator); + + if (targetView != null) { + View reactTargetView = findClosestReactAncestor(targetView); + int targetTag = getTouchTargetForView(reactTargetView, viewCoords[0], viewCoords[1]); + if (targetTag != pathAccumulator.get(0)) { + pathAccumulator.add(0, targetTag); + } + } + + return pathAccumulator; + } + private static View findClosestReactAncestor(View view) { while (view != null && view.getId() <= 0) { view = (View) view.getParent(); @@ -100,6 +136,14 @@ private static View findClosestReactAncestor(View view) { return view; } + /** Types of allowed return values from {@link #findTouchTargetView}. */ + private enum TouchTargetReturnType { + /** Allow returning the view passed in through the parameters. */ + SELF, + /** Allow returning children of the view passed in through parameters. */ + CHILD, + } + /** * Returns the touch target View that is either viewGroup or one if its descendants. This is a * recursive DFS since view the entire tree must be parsed until the target is found. If the @@ -111,43 +155,93 @@ private static View findClosestReactAncestor(View view) { * be relative to the current viewGroup. When the method returns, it will contain the eventCoords * relative to the targetView found. */ - private static View findTouchTargetView(float[] eventCoords, ViewGroup viewGroup) { - int childrenCount = viewGroup.getChildCount(); - // Consider z-index when determining the touch target. - ReactZIndexedViewGroup zIndexedViewGroup = - viewGroup instanceof ReactZIndexedViewGroup ? (ReactZIndexedViewGroup) viewGroup : null; - for (int i = childrenCount - 1; i >= 0; i--) { - int childIndex = - zIndexedViewGroup != null ? zIndexedViewGroup.getZIndexMappedChildIndex(i) : i; - View child = viewGroup.getChildAt(childIndex); - PointF childPoint = mTempPoint; - if (isTransformedTouchPointInView( - eventCoords[0], eventCoords[1], viewGroup, child, childPoint)) { - // If it is contained within the child View, the childPoint value will contain the view - // coordinates relative to the child + private static View findTouchTargetView( + float[] eventCoords, + View view, + EnumSet allowReturnTouchTargetTypes, + List pathAccumulator) { + // We prefer returning a child, so we check for a child that can handle the touch first + if (allowReturnTouchTargetTypes.contains(TouchTargetReturnType.CHILD) + && view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + int childrenCount = viewGroup.getChildCount(); + // Consider z-index when determining the touch target. + ReactZIndexedViewGroup zIndexedViewGroup = + viewGroup instanceof ReactZIndexedViewGroup ? (ReactZIndexedViewGroup) viewGroup : null; + for (int i = childrenCount - 1; i >= 0; i--) { + int childIndex = + zIndexedViewGroup != null ? zIndexedViewGroup.getZIndexMappedChildIndex(i) : i; + View child = viewGroup.getChildAt(childIndex); + PointF childPoint = mTempPoint; + getChildPoint(eventCoords[0], eventCoords[1], viewGroup, child, childPoint); + // The childPoint value will contain the view coordinates relative to the child. // We need to store the existing X,Y for the viewGroup away as it is possible this child // will not actually be the target and so we restore them if not float restoreX = eventCoords[0]; float restoreY = eventCoords[1]; eventCoords[0] = childPoint.x; eventCoords[1] = childPoint.y; - View targetView = findTouchTargetViewWithPointerEvents(eventCoords, child); + View targetView = findTouchTargetViewWithPointerEvents(eventCoords, child, pathAccumulator); if (targetView != null) { - return targetView; + // We don't allow touches on views that are outside the bounds of an `overflow: hidden` + // View + boolean inOverflowBounds = true; + if (viewGroup instanceof ReactOverflowView) { + @Nullable String overflow = ((ReactOverflowView) viewGroup).getOverflow(); + if ((ViewProps.HIDDEN.equals(overflow) || ViewProps.SCROLL.equals(overflow)) + && !isTouchPointInView(restoreX, restoreY, view)) { + inOverflowBounds = false; + } + } + if (inOverflowBounds) { + return targetView; + } else if (pathAccumulator != null) { + // Not a hit, reset the path found so far + pathAccumulator.clear(); + } } eventCoords[0] = restoreX; eventCoords[1] = restoreY; } } - return viewGroup; + + // Check if parent can handle the touch after the children + if (allowReturnTouchTargetTypes.contains(TouchTargetReturnType.SELF) + && isTouchPointInView(eventCoords[0], eventCoords[1], view)) { + return view; + } + + return null; } /** - * Returns whether the touch point is within the child View It is transform aware and will invert - * the transform Matrix to find the true local points This code is taken from {@link + * Checks whether a touch at {@code x} and {@code y} are within the bounds of the View. Both + * {@code x} and {@code y} must be relative to the top-left corner of the view. + */ + private static boolean isTouchPointInView(float x, float y, View view) { + if (view instanceof ReactHitSlopView && ((ReactHitSlopView) view).getHitSlopRect() != null) { + Rect hitSlopRect = ((ReactHitSlopView) view).getHitSlopRect(); + if ((x >= -hitSlopRect.left && x < (view.getWidth()) + hitSlopRect.right) + && (y >= -hitSlopRect.top && y < (view.getHeight()) + hitSlopRect.bottom)) { + return true; + } + + return false; + } else { + if ((x >= 0 && x < (view.getWidth())) && (y >= 0 && y < (view.getHeight()))) { + return true; + } + + return false; + } + } + + /** + * Returns the coordinates of a touch in the child View. It is transform aware and will invert the + * transform Matrix to find the true local points This code is taken from {@link * ViewGroup#isTransformedTouchPointInView()} */ - private static boolean isTransformedTouchPointInView( + private static void getChildPoint( float x, float y, ViewGroup parent, View child, PointF outLocalPoint) { float localX = x + parent.getScrollX() - child.getLeft(); float localY = y + parent.getScrollY() - child.getTop(); @@ -162,26 +256,7 @@ private static boolean isTransformedTouchPointInView( localX = localXY[0]; localY = localXY[1]; } - if (child instanceof ReactHitSlopView && ((ReactHitSlopView) child).getHitSlopRect() != null) { - Rect hitSlopRect = ((ReactHitSlopView) child).getHitSlopRect(); - if ((localX >= -hitSlopRect.left - && localX < (child.getRight() - child.getLeft()) + hitSlopRect.right) - && (localY >= -hitSlopRect.top - && localY < (child.getBottom() - child.getTop()) + hitSlopRect.bottom)) { - outLocalPoint.set(localX, localY); - return true; - } - - return false; - } else { - if ((localX >= 0 && localX < (child.getRight() - child.getLeft())) - && (localY >= 0 && localY < (child.getBottom() - child.getTop()))) { - outLocalPoint.set(localX, localY); - return true; - } - - return false; - } + outLocalPoint.set(localX, localY); } /** @@ -189,7 +264,7 @@ private static boolean isTransformedTouchPointInView( * its descendants are the touch target. */ private static @Nullable View findTouchTargetViewWithPointerEvents( - float eventCoords[], View view) { + float eventCoords[], View view, @Nullable List pathAccumulator) { PointerEvents pointerEvents = view instanceof ReactPointerEventsView ? ((ReactPointerEventsView) view).getPointerEvents() @@ -211,45 +286,68 @@ private static boolean isTransformedTouchPointInView( return null; } else if (pointerEvents == PointerEvents.BOX_ONLY) { - // This view is the target, its children don't matter - return view; + // This view may be the target, its children don't matter + View targetView = + findTouchTargetView( + eventCoords, view, EnumSet.of(TouchTargetReturnType.SELF), pathAccumulator); + if (targetView != null && pathAccumulator != null) { + pathAccumulator.add(view.getId()); + } + return targetView; } else if (pointerEvents == PointerEvents.BOX_NONE) { // This view can't be the target, but its children might. - if (view instanceof ViewGroup) { - View targetView = findTouchTargetView(eventCoords, (ViewGroup) view); - if (targetView != view) { - return targetView; + View targetView = + findTouchTargetView( + eventCoords, view, EnumSet.of(TouchTargetReturnType.CHILD), pathAccumulator); + if (targetView != null) { + if (pathAccumulator != null) { + pathAccumulator.add(view.getId()); } + return targetView; + } - // PointerEvents.BOX_NONE means that this react element cannot receive pointer events. - // However, there might be virtual children that can receive pointer events, in which case - // we still want to return this View and dispatch a pointer event to the virtual element. - // Note that this currently only applies to Nodes/FlatViewGroup as it's the only class that - // is both a ViewGroup and ReactCompoundView (ReactTextView is a ReactCompoundView but not a - // ViewGroup). - if (view instanceof ReactCompoundView) { - int reactTag = - ((ReactCompoundView) view).reactTagForTouch(eventCoords[0], eventCoords[1]); - if (reactTag != view.getId()) { - // make sure we exclude the View itself because of the PointerEvents.BOX_NONE - return view; + // PointerEvents.BOX_NONE means that this react element cannot receive pointer events. + // However, there might be virtual children that can receive pointer events, in which case + // we still want to return this View and dispatch a pointer event to the virtual element. + // Note that this currently only applies to Nodes/FlatViewGroup as it's the only class that + // is both a ViewGroup and ReactCompoundView (ReactTextView is a ReactCompoundView but not a + // ViewGroup). + if (view instanceof ReactCompoundView + && isTouchPointInView(eventCoords[0], eventCoords[1], view)) { + int reactTag = ((ReactCompoundView) view).reactTagForTouch(eventCoords[0], eventCoords[1]); + // make sure we exclude the View itself because of the PointerEvents.BOX_NONE + if (reactTag != view.getId()) { + if (pathAccumulator != null) { + pathAccumulator.add(view.getId()); } + return view; } } + return null; } else if (pointerEvents == PointerEvents.AUTO) { // Either this view or one of its children is the target - if (view instanceof ReactCompoundViewGroup) { - if (((ReactCompoundViewGroup) view).interceptsTouchEvent(eventCoords[0], eventCoords[1])) { - return view; + if (view instanceof ReactCompoundViewGroup + && isTouchPointInView(eventCoords[0], eventCoords[1], view) + && ((ReactCompoundViewGroup) view).interceptsTouchEvent(eventCoords[0], eventCoords[1])) { + if (pathAccumulator != null) { + pathAccumulator.add(view.getId()); } + return view; } - if (view instanceof ViewGroup) { - return findTouchTargetView(eventCoords, (ViewGroup) view); + + View result = + findTouchTargetView( + eventCoords, + view, + EnumSet.of(TouchTargetReturnType.SELF, TouchTargetReturnType.CHILD), + pathAccumulator); + if (result != null && pathAccumulator != null) { + pathAccumulator.add(view.getId()); } - return view; + return result; } else { throw new JSApplicationIllegalArgumentException( diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 16630deff0227..bce8995feeedf 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -21,7 +21,6 @@ import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableArray; import com.facebook.react.common.ReactConstants; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.events.EventDispatcher; @@ -51,6 +50,13 @@ public class UIImplementation { private long mLastCalculateLayoutTime = 0; protected @Nullable LayoutUpdateListener mLayoutUpdateListener; + /** + * When react instance is being shutdown, there could be some pending operations queued in the JS + * thread. This flag ensures view related operations are not triggered if the Catalyst instance + * was destroyed. + */ + private volatile boolean mViewOperationsEnabled = true; + /** Interface definition for a callback to be invoked when the layout has been updated */ public interface LayoutUpdateListener { @@ -60,7 +66,7 @@ public interface LayoutUpdateListener { public UIImplementation( ReactApplicationContext reactContext, - UIManagerModule.ViewManagerResolver viewManagerResolver, + ViewManagerResolver viewManagerResolver, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) { this( @@ -234,6 +240,10 @@ public Map getProfiledBatchPerfCounters() { /** Invoked by React to create a new node with a given tag, class name and properties. */ public void createView(int tag, String className, int rootViewTag, ReadableMap props) { + if (!mViewOperationsEnabled) { + return; + } + synchronized (uiImplementationThreadLock) { ReactShadowNode cssNode = createShadowNode(className); ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag); @@ -264,6 +274,10 @@ protected void handleCreateView( /** Invoked by React to create a new node with a given tag has its properties changed. */ public void updateView(int tag, String className, ReadableMap props) { + if (!mViewOperationsEnabled) { + return; + } + ViewManager viewManager = mViewManagers.get(className); if (viewManager == null) { throw new IllegalViewOperationException("Got unknown view type: " + className); @@ -314,6 +328,10 @@ public void manageChildren( @Nullable ReadableArray addChildTags, @Nullable ReadableArray addAtIndices, @Nullable ReadableArray removeFrom) { + if (!mViewOperationsEnabled) { + return; + } + synchronized (uiImplementationThreadLock) { ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag); @@ -420,6 +438,10 @@ public void manageChildren( * @param childrenTags tags of the children */ public void setChildren(int viewTag, ReadableArray childrenTags) { + if (!mViewOperationsEnabled) { + return; + } + synchronized (uiImplementationThreadLock) { ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag); @@ -530,6 +552,10 @@ public void viewIsDescendantOf( * view and returns the values via an async callback. */ public void measure(int reactTag, Callback callback) { + if (!mViewOperationsEnabled) { + return; + } + // This method is called by the implementation of JS touchable interface (see Touchable.js for // more details) at the moment of touch activation. That is after user starts the gesture from // a touchable view with a given reactTag, or when user drag finger back into the press @@ -543,6 +569,10 @@ public void measure(int reactTag, Callback callback) { * things like the status bar */ public void measureInWindow(int reactTag, Callback callback) { + if (!mViewOperationsEnabled) { + return; + } + mOperationsQueue.enqueueMeasureInWindow(reactTag, callback); } @@ -554,6 +584,10 @@ public void measureInWindow(int reactTag, Callback callback) { */ public void measureLayout( int tag, int ancestorTag, Callback errorCallback, Callback successCallback) { + if (!mViewOperationsEnabled) { + return; + } + try { measureLayout(tag, ancestorTag, mMeasureBuffer); float relativeX = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]); @@ -571,6 +605,10 @@ public void measureLayout( */ public void measureLayoutRelativeToParent( int tag, Callback errorCallback, Callback successCallback) { + if (!mViewOperationsEnabled) { + return; + } + try { measureLayoutRelativeToParent(tag, mMeasureBuffer); float relativeX = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]); @@ -747,6 +785,10 @@ public void onHostPause() { public void onHostDestroy() {} + public void onCatalystInstanceDestroyed() { + mViewOperationsEnabled = false; + } + public void setViewHierarchyUpdateDebugListener( @Nullable NotThreadSafeViewHierarchyUpdateDebugListener listener) { mOperationsQueue.setViewHierarchyUpdateDebugListener(listener); @@ -913,6 +955,7 @@ protected void applyUpdatesRecursive(ReactShadowNode cssNode, float absoluteX, f if (frameDidChange && cssNode.shouldNotifyOnLayout()) { mEventDispatcher.dispatchEvent( OnLayoutEvent.obtain( + -1, /* surfaceId not used in classic renderer */ tag, cssNode.getScreenX(), cssNode.getScreenY(), @@ -921,9 +964,7 @@ protected void applyUpdatesRecursive(ReactShadowNode cssNode, float absoluteX, f } } cssNode.markUpdateSeen(); - if (ReactFeatureFlags.enableTransitionLayoutOnlyViewCleanup) { - mNativeViewHierarchyOptimizer.onViewUpdatesCompleted(cssNode); - } + mNativeViewHierarchyOptimizer.onViewUpdatesCompleted(cssNode); } public void addUIBlock(UIBlock block) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementationProvider.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementationProvider.java index 65b222408f66a..efc7703918eaa 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementationProvider.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementationProvider.java @@ -9,6 +9,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.systrace.Systrace; import java.util.List; /** Provides UIImplementation to use in {@link UIManagerModule}. */ @@ -17,14 +18,20 @@ public class UIImplementationProvider { public UIImplementation createUIImplementation( ReactApplicationContext reactContext, - UIManagerModule.ViewManagerResolver viewManagerResolver, + ViewManagerResolver viewManagerResolver, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) { - return new UIImplementation( - reactContext, - viewManagerResolver, - eventDispatcher, - minTimeLeftInFrameForNonBatchedOperationMs); + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "UIImplementationProvider.createUIImplementation[1]"); + try { + return new UIImplementation( + reactContext, + viewManagerResolver, + eventDispatcher, + minTimeLeftInFrameForNonBatchedOperationMs); + } finally { + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } } public UIImplementation createUIImplementation( @@ -32,8 +39,17 @@ public UIImplementation createUIImplementation( List viewManagerList, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) { - return new UIImplementation( - reactContext, viewManagerList, eventDispatcher, minTimeLeftInFrameForNonBatchedOperationMs); + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "UIImplementationProvider.createUIImplementation[2]"); + try { + return new UIImplementation( + reactContext, + viewManagerList, + eventDispatcher, + minTimeLeftInFrameForNonBatchedOperationMs); + } finally { + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } } UIImplementation createUIImplementation( @@ -41,10 +57,16 @@ UIImplementation createUIImplementation( ViewManagerRegistry viewManagerRegistry, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) { - return new UIImplementation( - reactContext, - viewManagerRegistry, - eventDispatcher, - minTimeLeftInFrameForNonBatchedOperationMs); + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "UIImplementationProvider.createUIImplementation[3]"); + try { + return new UIImplementation( + reactContext, + viewManagerRegistry, + eventDispatcher, + minTimeLeftInFrameForNonBatchedOperationMs); + } finally { + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java index 124fb1b861dc8..9effc356f7f01 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java @@ -20,7 +20,7 @@ import com.facebook.react.bridge.JSIModuleType; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UIManager; import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; @@ -29,6 +29,7 @@ /** Helper class for {@link UIManager}. */ public class UIManagerHelper { + private static final String TAG = UIManagerHelper.class.getName(); public static final int PADDING_START_INDEX = 0; public static final int PADDING_END_INDEX = 1; public static final int PADDING_TOP_INDEX = 2; @@ -52,14 +53,10 @@ private static UIManager getUIManager( @UIManagerType int uiManagerType, boolean returnNullIfCatalystIsInactive) { if (context.isBridgeless()) { - @Nullable - UIManager uiManager = - context.getJSIModule(JSIModuleType.UIManager) != null - ? (UIManager) context.getJSIModule(JSIModuleType.UIManager) - : null; + @Nullable UIManager uiManager = (UIManager) context.getJSIModule(JSIModuleType.UIManager); if (uiManager == null) { - ReactSoftException.logSoftException( - "UIManagerHelper", + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException( "Cannot get UIManager because the instance hasn't been initialized yet.")); return null; @@ -68,17 +65,17 @@ private static UIManager getUIManager( } if (!context.hasCatalystInstance()) { - ReactSoftException.logSoftException( - "UIManagerHelper", + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException( "Cannot get UIManager because the context doesn't contain a CatalystInstance.")); return null; } // TODO T60461551: add tests to verify emission of events when the ReactContext is being turn // down. - if (!context.hasActiveCatalystInstance()) { - ReactSoftException.logSoftException( - "UIManagerHelper", + if (!context.hasActiveReactInstance()) { + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException( "Cannot get UIManager because the context doesn't contain an active CatalystInstance.")); if (returnNullIfCatalystIsInactive) { @@ -92,8 +89,8 @@ private static UIManager getUIManager( : catalystInstance.getNativeModule(UIManagerModule.class); } catch (IllegalArgumentException ex) { // TODO T67518514 Clean this up once we migrate everything over to bridgeless mode - ReactSoftException.logSoftException( - "UIManagerHelper", + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException( "Cannot get UIManager for UIManagerType: " + uiManagerType)); return catalystInstance.getNativeModule(UIManagerModule.class); @@ -106,7 +103,12 @@ private static UIManager getUIManager( */ @Nullable public static EventDispatcher getEventDispatcherForReactTag(ReactContext context, int reactTag) { - return getEventDispatcher(context, getUIManagerType(reactTag)); + EventDispatcher eventDispatcher = getEventDispatcher(context, getUIManagerType(reactTag)); + if (eventDispatcher == null) { + ReactSoftExceptionLogger.logSoftException( + TAG, new IllegalStateException("Cannot get EventDispatcher for reactTag " + reactTag)); + } + return eventDispatcher; } /** @@ -124,7 +126,21 @@ public static EventDispatcher getEventDispatcher( return ((EventDispatcherProvider) context).getEventDispatcher(); } UIManager uiManager = getUIManager(context, uiManagerType, false); - return uiManager == null ? null : (EventDispatcher) uiManager.getEventDispatcher(); + if (uiManager == null) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Unable to find UIManager for UIManagerType " + uiManagerType)); + return null; + } + EventDispatcher eventDispatcher = (EventDispatcher) uiManager.getEventDispatcher(); + if (eventDispatcher == null) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Cannot get EventDispatcher for UIManagerType " + uiManagerType)); + } + return eventDispatcher; } /** @@ -143,6 +159,42 @@ public static ReactContext getReactContext(View view) { return (ReactContext) context; } + /** + * @return Get the ThemedReactContext associated with a View, if possible, and then call + * getSurfaceId on it. See above (getReactContext) for additional context. + */ + public static int getSurfaceId(View view) { + int reactTag = view.getId(); + + // In non-Fabric we don't have (or use) SurfaceId + if (getUIManagerType(reactTag) == UIManagerType.DEFAULT) { + return -1; + } + + Context context = view.getContext(); + if (!(context instanceof ThemedReactContext) && context instanceof ContextWrapper) { + context = ((ContextWrapper) context).getBaseContext(); + } + + int surfaceId = getSurfaceId(context); + + // All Fabric-managed Views (should) have a ThemedReactContext attached. + if (surfaceId == -1) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Fabric View [" + reactTag + "] does not have SurfaceId associated with it")); + } + return surfaceId; + } + + public static int getSurfaceId(Context context) { + if (context instanceof ThemedReactContext) { + return ((ThemedReactContext) context).getSurfaceId(); + } + return -1; + } + /** * @return the default padding used by Android EditText's. This method returns the padding in an * array to avoid extra classloading during hot-path of RN Android. diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index c5340ae3b7510..b034d1c8faf87 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -15,6 +15,7 @@ import android.content.ComponentCallbacks2; import android.content.res.Configuration; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.ArrayMap; import com.facebook.common.logging.FLog; @@ -39,6 +40,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.ReactConstants; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; @@ -85,21 +87,7 @@ @ReactModule(name = UIManagerModule.NAME) public class UIManagerModule extends ReactContextBaseJavaModule implements OnBatchCompleteListener, LifecycleEventListener, UIManager { - - /** Enables lazy discovery of a specific {@link ViewManager} by its name. */ - public interface ViewManagerResolver { - /** - * {@class UIManagerModule} class uses this method to get a ViewManager by its name. This is the - * same name that comes from JS by {@code UIManager.ViewManagerName} call. - */ - @Nullable - ViewManager getViewManager(String viewManagerName); - - /** - * Provides a list of view manager names to register in JS as {@code UIManager.ViewManagerName} - */ - List getViewManagerNames(); - } + public static final String TAG = UIManagerModule.class.getSimpleName(); /** Resolves a name coming from native side to a name of the event that is exposed to JS. */ public interface CustomEventNamesResolver { @@ -126,6 +114,7 @@ public interface CustomEventNamesResolver { private volatile int mViewManagerConstantsCacheSize; private int mBatchId = 0; + private int mNumRootViews = 0; @SuppressWarnings("deprecated") public UIManagerModule( @@ -207,7 +196,7 @@ public UIImplementation getUIImplementation() { } @Override - public String getName() { + public @NonNull String getName() { return NAME; } @@ -242,8 +231,13 @@ public void onHostDestroy() { public void onCatalystInstanceDestroy() { super.onCatalystInstanceDestroy(); mEventDispatcher.onCatalystInstanceDestroyed(); + mUIImplementation.onCatalystInstanceDestroyed(); - getReactApplicationContext().unregisterComponentCallbacks(mMemoryTrimCallback); + ReactApplicationContext reactApplicationContext = getReactApplicationContext(); + if (ReactFeatureFlags.enableReactContextCleanupFix) { + reactApplicationContext.removeLifecycleEventListener(this); + } + reactApplicationContext.unregisterComponentCallbacks(mMemoryTrimCallback); YogaNodePool.get().clear(); ViewManagerPropertyUpdater.clear(); } @@ -291,11 +285,22 @@ private static Map createConstants( * Helper method to pre-compute the constants for a view manager. This method ensures that we * don't block for getting the constants for view managers during TTI * - * @deprecated this method will not be available in FabricUIManager class. + * @deprecated this method will be removed in the future * @param viewManagerNames {@link List} names of ViewManagers */ @Deprecated - public void preComputeConstantsForViewManager(List viewManagerNames) { + @Override + public void preInitializeViewManagers(List viewManagerNames) { + if (ReactFeatureFlags.enableExperimentalStaticViewConfigs) { + for (String viewManagerName : viewManagerNames) { + mUIImplementation.resolveViewManager(viewManagerName); + } + // When Static view configs are enabled it is not necessary to pre-compute the constants for + // viewManagers, although the pre-initialization of viewManager objects is still necessary + // for performance reasons. + return; + } + Map constantsMap = new ArrayMap<>(); for (String viewManagerName : viewManagerNames) { WritableMap constants = computeConstantsForViewManager(viewManagerName); @@ -406,22 +411,17 @@ public int addRootView(final T rootView) { */ @Override public void synchronouslyUpdateViewOnUIThread(int tag, ReadableMap props) { - int uiManagerType = ViewUtil.getUIManagerType(tag); - if (uiManagerType == FABRIC) { - UIManager fabricUIManager = - UIManagerHelper.getUIManager(getReactApplicationContext(), uiManagerType); - if (fabricUIManager != null) { - fabricUIManager.synchronouslyUpdateViewOnUIThread(tag, props); - } - } else { - mUIImplementation.synchronouslyUpdateViewOnUIThread(tag, new ReactStylesDiffMap(props)); - } + mUIImplementation.synchronouslyUpdateViewOnUIThread(tag, new ReactStylesDiffMap(props)); } /** * Registers a new root view. JS can use the returned tag with manageChildren to add/remove * children to this view. * + *

Calling addRootView through UIManagerModule calls addRootView in the non-Fabric renderer, + * always. This is deprecated in favor of calling startSurface in Fabric, which must be done + * directly through the FabricUIManager. + * *

Note that this must be called after getWidth()/getHeight() actually return something. See * CatalystApplicationFragment as an example. * @@ -433,11 +433,17 @@ public int addRootView( Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "UIManagerModule.addRootView"); final int tag = ReactRootViewTagGenerator.getNextRootViewTag(); final ReactApplicationContext reactApplicationContext = getReactApplicationContext(); + + // We pass in a surfaceId of -1 here - it is used only in Fabric. final ThemedReactContext themedRootContext = new ThemedReactContext( - reactApplicationContext, rootView.getContext(), ((ReactRoot) rootView).getSurfaceID()); + reactApplicationContext, + rootView.getContext(), + ((ReactRoot) rootView).getSurfaceID(), + -1); mUIImplementation.registerRootView(rootView, tag, themedRootContext); + mNumRootViews++; Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); return tag; } @@ -461,6 +467,7 @@ public void stopSurface(final int surfaceId) { @ReactMethod public void removeRootView(int rootViewTag) { mUIImplementation.removeRootView(rootViewTag); + mNumRootViews--; } public void updateNodeSize(int nodeViewTag, int newWidth, int newHeight) { @@ -509,25 +516,7 @@ public void updateView(final int tag, final String className, final ReadableMap FLog.d(ReactConstants.TAG, message); PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.UI_MANAGER, message); } - int uiManagerType = ViewUtil.getUIManagerType(tag); - if (uiManagerType == FABRIC) { - ReactApplicationContext reactApplicationContext = getReactApplicationContext(); - if (reactApplicationContext.hasActiveCatalystInstance()) { - final UIManager fabricUIManager = - UIManagerHelper.getUIManager(reactApplicationContext, uiManagerType); - if (fabricUIManager != null) { - reactApplicationContext.runOnUiQueueThread( - new Runnable() { - @Override - public void run() { - fabricUIManager.synchronouslyUpdateViewOnUIThread(tag, props); - } - }); - } - } - } else { - mUIImplementation.updateView(tag, className, props); - } + mUIImplementation.updateView(tag, className, props); } /** @@ -710,8 +699,7 @@ public void clearJSResponder() { @ReactMethod public void dispatchViewManagerCommand( int reactTag, Dynamic commandId, @Nullable ReadableArray commandArgs) { - // TODO: this is a temporary approach to support ViewManagerCommands in Fabric until - // the dispatchViewManagerCommand() method is supported by Fabric JS API. + // Fabric dispatchCommands should go through the JSI API - this will crash in Fabric. @Nullable UIManager uiManager = UIManagerHelper.getUIManager( @@ -820,7 +808,12 @@ public void onBatchComplete() { listener.willDispatchViewUpdates(this); } try { - mUIImplementation.dispatchViewUpdates(batchId); + // If there are no RootViews registered, there will be no View updates to dispatch. + // This is a hack to prevent this from being called when Fabric is used everywhere. + // This should no longer be necessary in Bridgeless Mode. + if (mNumRootViews > 0) { + mUIImplementation.dispatchViewUpdates(batchId); + } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } @@ -962,6 +955,7 @@ public void onConfigurationChanged(Configuration newConfig) {} public void onLowMemory() {} } + @Override public View resolveView(int tag) { UiThreadUtil.assertOnUiThread(); return mUIImplementation @@ -971,9 +965,16 @@ public View resolveView(int tag) { } @Override - public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event) { + public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap event) { + receiveEvent(-1, reactTag, eventName, event); + } + + @Override + public void receiveEvent( + int surfaceId, int reactTag, String eventName, @Nullable WritableMap event) { + assert ViewUtil.getUIManagerType(reactTag) == DEFAULT; getReactApplicationContext() .getJSModule(RCTEventEmitter.class) - .receiveEvent(targetTag, eventName, event); + .receiveEvent(reactTag, eventName, event); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java index 19e887b87cd8b..40a3730b7cba8 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java @@ -31,8 +31,7 @@ * UIManager.getViewManagerConfig('SpecificViewManager')} call happens. The View Manager is then * registered on the JS side with the help of {@code UIManagerModule.getConstantsForViewManager}. */ - /* package */ static Map createConstants( - UIManagerModule.ViewManagerResolver resolver) { + /* package */ static Map createConstants(ViewManagerResolver resolver) { Map constants = UIManagerModuleConstants.getConstants(); constants.put("ViewManagerNames", resolver.getViewManagerNames()); constants.put("LazyViewManagersEnabled", true); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index bb3df5f827e71..289b2774bf915 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -18,7 +18,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.RetryableMountingLayerException; @@ -118,7 +118,13 @@ public void execute() { uiManager .getEventDispatcher() .dispatchEvent( - OnLayoutEvent.obtain(mTag, mScreenX, mScreenY, mScreenWidth, mScreenHeight)); + OnLayoutEvent.obtain( + -1 /* SurfaceId not used in classic renderer */, + mTag, + mScreenX, + mScreenY, + mScreenWidth, + mScreenHeight)); } } } @@ -307,7 +313,7 @@ public void execute() { try { mNativeViewHierarchyManager.dispatchCommand(mTag, mCommand, mArgs); } catch (Throwable e) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new RuntimeException("Error dispatching View Command", e)); } } @@ -348,7 +354,7 @@ public void execute() { try { mNativeViewHierarchyManager.dispatchCommand(mTag, mCommand, mArgs); } catch (Throwable e) { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new RuntimeException("Error dispatching View Command", e)); } } @@ -886,11 +892,12 @@ public void run() { mViewCommandOperations.add(op); } else { // Retryable exceptions should be logged, but never crash in debug. - ReactSoftException.logSoftException(TAG, new ReactNoCrashSoftException(e)); + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException(e)); } } catch (Throwable e) { // Non-retryable exceptions should be logged in prod, and crash in Debug. - ReactSoftException.logSoftException(TAG, e); + ReactSoftExceptionLogger.logSoftException(TAG, e); } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java index a806b87a380a3..6af66a6f08017 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java @@ -15,7 +15,6 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.touch.JSResponderHandler; import com.facebook.react.touch.ReactInterceptingViewGroup; import com.facebook.react.uimanager.annotations.ReactProp; @@ -39,11 +38,10 @@ public abstract class ViewManager * * @param viewToUpdate * @param props - * @param stateWrapper */ public void updateProperties(@NonNull T viewToUpdate, ReactStylesDiffMap props) { - final ViewManagerDelegate delegate; - if (ReactFeatureFlags.useViewManagerDelegates && (delegate = getDelegate()) != null) { + final ViewManagerDelegate delegate = getDelegate(); + if (delegate != null) { ViewManagerPropertyUpdater.updateProps(delegate, viewToUpdate, props); } else { ViewManagerPropertyUpdater.updateProps(this, viewToUpdate, props); @@ -68,19 +66,14 @@ protected ViewManagerDelegate getDelegate() { return null; } - /** Creates a view and installs event emitters on it. */ - private final @NonNull T createView( - @NonNull ThemedReactContext reactContext, JSResponderHandler jsResponderHandler) { - return createView(reactContext, null, null, jsResponderHandler); - } - /** Creates a view with knowledge of props and state. */ public @NonNull T createView( + int reactTag, @NonNull ThemedReactContext reactContext, @Nullable ReactStylesDiffMap props, @Nullable StateWrapper stateWrapper, JSResponderHandler jsResponderHandler) { - T view = createViewInstance(reactContext, props, stateWrapper); + T view = createViewInstance(reactTag, reactContext, props, stateWrapper); if (view instanceof ReactInterceptingViewGroup) { ((ReactInterceptingViewGroup) view).setOnInterceptTouchEventListener(jsResponderHandler); } @@ -128,19 +121,28 @@ public C createShadowNodeInstance() { /** * Subclasses should return a new View instance of the proper type. This is an optional method * that will call createViewInstance for you. Override it if you need props upon creation of the - * view. + * view, or state. * - * @param reactContext + *

If you override this method, you *must* guarantee that you you're handling updateProperties, + * view.setId, addEventEmitters, and updateState/updateExtraData properly! + * + * @param reactTag reactTag that should be set as ID of the view instance + * @param reactContext ReactContext used to initialize view instance + * @param initialProps initial props for the view instance + * @param stateWrapper initial state for the view instance */ protected @NonNull T createViewInstance( + int reactTag, @NonNull ThemedReactContext reactContext, @Nullable ReactStylesDiffMap initialProps, @Nullable StateWrapper stateWrapper) { T view = createViewInstance(reactContext); + view.setId(reactTag); addEventEmitters(reactContext, view); if (initialProps != null) { updateProperties(view, initialProps); } + // Only present in Fabric; but always present in Fabric. if (stateWrapper != null) { Object extraData = updateState(view, initialProps, stateWrapper); if (extraData != null) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java index a6ceb3e395a67..b01234dbb6ddc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java @@ -19,9 +19,9 @@ public final class ViewManagerRegistry { private final Map mViewManagers; - private final @Nullable UIManagerModule.ViewManagerResolver mViewManagerResolver; + private final @Nullable ViewManagerResolver mViewManagerResolver; - public ViewManagerRegistry(UIManagerModule.ViewManagerResolver viewManagerResolver) { + public ViewManagerRegistry(ViewManagerResolver viewManagerResolver) { mViewManagers = MapBuilder.newHashMap(); mViewManagerResolver = viewManagerResolver; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerResolver.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerResolver.java new file mode 100644 index 0000000000000..d00b30fe80460 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerResolver.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager; + +import java.util.List; +import javax.annotation.Nullable; + +/** Enables lazy discovery of a specific {@link ViewManager} by its name. */ +public interface ViewManagerResolver { + /** + * {@class UIManagerModule} class uses this method to get a ViewManager by its name. This is the + * same name that comes from JS by {@code UIManager.ViewManagerName} call. + */ + @Nullable + ViewManager getViewManager(String viewManagerName); + + /** + * Provides a list of view manager names to register in JS as {@code UIManager.ViewManagerName} + */ + List getViewManagerNames(); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java index 6e2a1691fd16c..f856e85d53cff 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java @@ -325,6 +325,21 @@ public BoxedIntPropSetter(ReactPropGroup prop, Method setter, int index) { } } + private static class BoxedColorPropSetter extends PropSetter { + + public BoxedColorPropSetter(ReactProp prop, Method setter) { + super(prop, "mixed", setter); + } + + @Override + protected @Nullable Object getValueOrDefault(Object value, Context context) { + if (value != null) { + return ColorPropConverter.getColor(value, context); + } + return null; + } + } + /*package*/ static Map getNativePropsForView( Class viewManagerTopClass, Class shadowNodeTopClass) { @@ -418,7 +433,7 @@ private static PropSetter createPropSetter( return new BoxedBooleanPropSetter(annotation, method); } else if (propTypeClass == Integer.class) { if ("Color".equals(annotation.customType())) { - return new ColorPropSetter(annotation, method); + return new BoxedColorPropSetter(annotation, method); } return new BoxedIntPropSetter(annotation, method); } else if (propTypeClass == ReadableArray.class) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK index 11bc3ba33e01e..b876ba18bc5b2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "annotations", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK index d8e9532b1a6a6..6d46fd8953e9c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "common", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -17,7 +18,6 @@ rn_android_library( "PUBLIC", ], deps = [ - # react_native_dep("third-party/android/androidx:appcompat"), react_native_dep("third-party/java/jsr-305:jsr-305"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java index 1fce7974087b6..15abfde7a0426 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java @@ -49,6 +49,9 @@ public void removeBatchEventDispatchedListener(BatchEventDispatchedListener list @Override public void registerEventEmitter(int uiManagerType, RCTEventEmitter eventEmitter) {} + @Override + public void registerEventEmitter(int uiManagerType, RCTModernEventEmitter eventEmitter) {} + @Override public void unregisterEventEmitter(int uiManagerType) {} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java index a28bd72b12e68..d65c810b095f7 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java @@ -19,8 +19,13 @@ public class ContentSizeChangeEvent extends Event { private final int mWidth; private final int mHeight; + @Deprecated public ContentSizeChangeEvent(int viewTag, int width, int height) { - super(viewTag); + this(-1, viewTag, width, height); + } + + public ContentSizeChangeEvent(int surfaceId, int viewTag, int width, int height) { + super(surfaceId, viewTag); mWidth = width; mHeight = height; } @@ -31,10 +36,10 @@ public String getEventName() { } @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { + protected WritableMap getEventData() { WritableMap data = Arguments.createMap(); data.putDouble("width", PixelUtil.toDIPFromPixel(mWidth)); data.putDouble("height", PixelUtil.toDIPFromPixel(mHeight)); - rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data); + return data; } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java index 7d36c049ed9df..b205bdeb3c76f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java @@ -7,34 +7,81 @@ package com.facebook.react.uimanager.events; +import androidx.annotation.Nullable; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.SystemClock; +import com.facebook.react.uimanager.IllegalViewOperationException; +import com.facebook.react.uimanager.common.UIManagerType; /** * A UI event that can be dispatched to JS. * - *

For dispatching events {@link EventDispatcher#dispatchEvent} should be used. Once event object - * is passed to the EventDispatched it should no longer be used as EventDispatcher may decide to - * recycle that object (by calling {@link #dispose}). + *

For dispatching events {@code getEventData} should be used. Once event object is passed to the + * EventDispatched it should no longer be used as EventDispatcher may decide to recycle that object + * (by calling {@link #dispose}). + * + *

If you need advanced customizations and overriding only {@code getEventData} doesn't work for + * you, you must override both {@code dispatch} and {@code dispatchModern}. Both of these will be + * deleted in the distant future and it is highly recommended to use only {@code getEventData}. + * + *

Old, pre-Fabric Events only used viewTag as the identifier, but Fabric needs surfaceId as well + * as viewTag. You may use {@code UIManagerHelper.getSurfaceId} on a Fabric-managed View to get the + * surfaceId. Fabric will work without surfaceId - making {@code Event} backwards-compatible - but + * Events without SurfaceId are slightly slower to propagate. */ public abstract class Event { private static int sUniqueID = 0; private boolean mInitialized; + private @UIManagerType int mUIManagerType; + private int mSurfaceId; private int mViewTag; private long mTimestampMs; private int mUniqueID = sUniqueID++; protected Event() {} + @Deprecated protected Event(int viewTag) { init(viewTag); } - /** This method needs to be called before event is sent to event dispatcher. */ + protected Event(int surfaceId, int viewTag) { + init(surfaceId, viewTag); + } + + @Deprecated protected void init(int viewTag) { + init(-1, viewTag); + } + + protected void init(int surfaceId, int viewTag) { + init(surfaceId, viewTag, SystemClock.uptimeMillis()); + } + + /** + * This method needs to be called before event is sent to event dispatcher. Event timestamps can + * optionally be dated/backdated to a custom time: for example, touch events should be dated with + * the system event time. + */ + protected void init(int surfaceId, int viewTag, long timestampMs) { + mSurfaceId = surfaceId; mViewTag = viewTag; - mTimestampMs = SystemClock.uptimeMillis(); + + // We infer UIManagerType. Even though it's not passed in explicitly, we have a + // contract that Fabric events *always* have a SurfaceId passed in, and non-Fabric events + // NEVER have a SurfaceId passed in (the default/placeholder of -1 is passed in instead). + // Why does this matter? + // Events can be sent to Views that are part of the View hierarchy *but not directly managed + // by React Native*. For example, embedded custom hierachies, Litho hierachies, etc. + // In those cases it's important to konw that the Event should be sent to the Fabric or + // non-Fabric UIManager, and we cannot use the ViewTag for inference since it's not controlled + // by RN and is essentially a random number. + // At some point it would be great to pass the SurfaceContext here instead. + mUIManagerType = (surfaceId == -1 ? UIManagerType.DEFAULT : UIManagerType.FABRIC); + + mTimestampMs = timestampMs; mInitialized = true; } @@ -43,6 +90,11 @@ public final int getViewTag() { return mViewTag; } + /** @return the surfaceId for the view that generated this event */ + public final int getSurfaceId() { + return mSurfaceId; + } + /** * @return the time at which the event happened in the {@link android.os.SystemClock#uptimeMillis} * base. @@ -97,9 +149,82 @@ public void onDispose() {} onDispose(); } + public final @UIManagerType int getUIManagerType() { + return mUIManagerType; + } + /** @return the name of this event as registered in JS */ public abstract String getEventName(); - /** Dispatch this event to JS using the given event emitter. */ - public abstract void dispatch(RCTEventEmitter rctEventEmitter); + /** + * Dispatch this event to JS using the given event emitter. Compatible with old and new renderer. + * Instead of using this or dispatchModern, it is recommended that you simply override + * `getEventData`. In the future + */ + @Deprecated + public void dispatch(RCTEventEmitter rctEventEmitter) { + WritableMap eventData = getEventData(); + if (eventData == null) { + throw new IllegalViewOperationException( + "Event: you must return a valid, non-null value from `getEventData`, or override `dispatch` and `dispatchModern`. Event: " + + getEventName()); + } + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData); + } + + /** + * Can be overridden by classes to make migrating to RCTModernEventEmitter support easier. If this + * class returns null, the RCTEventEmitter interface will be used instead of + * RCTModernEventEmitter. In the future, returning null here will be an error. + */ + @Nullable + protected WritableMap getEventData() { + return null; + } + + @EventCategoryDef + protected int getEventCategory() { + return EventCategoryDef.UNSPECIFIED; + } + + /** + * Dispatch this event to JS using a V2 EventEmitter. If surfaceId is not -1 and `getEventData` is + * non-null, this will use the RCTModernEventEmitter API. Otherwise, it falls back to the + * old-style dispatch function. For Event classes that need to do something different, this method + * can always be overridden entirely, but it is not recommended. + */ + public void dispatchModern(RCTModernEventEmitter rctEventEmitter) { + if (getSurfaceId() != -1) { + WritableMap eventData = getEventData(); + if (eventData != null) { + rctEventEmitter.receiveEvent(getSurfaceId(), getViewTag(), getEventName(), eventData); + return; + } + } + dispatch(rctEventEmitter); + } + + /** + * Dispatch this event to JS using a V2 version of dispatchModern. See all comments from + * `dispatchModern` - all still apply. This method additionally allows C++ to coalesce events + * (Fabric only). This will ONLY be called in an experimental path, and in Fabric only. + */ + @Deprecated + public void dispatchModernV2(RCTModernEventEmitter rctEventEmitter) { + if (getSurfaceId() != -1) { + WritableMap eventData = getEventData(); + if (eventData != null) { + rctEventEmitter.receiveEvent( + getSurfaceId(), + getViewTag(), + getEventName(), + canCoalesce(), + getCoalescingKey(), + eventData, + getEventCategory()); + return; + } + } + dispatch(rctEventEmitter); + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventCategoryDef.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventCategoryDef.java new file mode 100644 index 0000000000000..60c5a50df7845 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventCategoryDef.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager.events; + +import static com.facebook.react.uimanager.events.EventCategoryDef.CONTINUOUS; +import static com.facebook.react.uimanager.events.EventCategoryDef.CONTINUOUS_END; +import static com.facebook.react.uimanager.events.EventCategoryDef.CONTINUOUS_START; +import static com.facebook.react.uimanager.events.EventCategoryDef.DISCRETE; +import static com.facebook.react.uimanager.events.EventCategoryDef.UNSPECIFIED; + +import androidx.annotation.IntDef; + +/** + * Java specific declaration of the `RawEvent::Category` enum. Keep in sync with + * `renderer/core/RawEvent.h`. + */ +@IntDef(value = {CONTINUOUS_START, CONTINUOUS_END, UNSPECIFIED, DISCRETE, CONTINUOUS}) +public @interface EventCategoryDef { + /** Start of a continuous event. To be used with touchStart. */ + int CONTINUOUS_START = 0; + + /** End of a continuous event. To be used with touchEnd. */ + int CONTINUOUS_END = 1; + + /** + * Priority for this event will be determined from other events in the queue. If it is triggered + * by continuous event, its priority will be default. If it is not triggered by continuous event, + * its priority will be discrete. + */ + int UNSPECIFIED = 2; + + /** Forces discrete type for the event. Regardless if continuous event is ongoing. */ + int DISCRETE = 3; + + /** Forces continuous type for the event. Regardless if continuous event isn't ongoing. */ + int CONTINUOUS = 4; +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java index 2ba1834097728..2e9ac90cc2c93 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java @@ -26,8 +26,11 @@ public interface EventDispatcher { void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener); + @Deprecated void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter); + void registerEventEmitter(@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter); + void unregisterEventEmitter(@UIManagerType int uiManagerType); void onCatalystInstanceDestroyed(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java index 9754b2de08547..c972fe03ad139 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java @@ -13,6 +13,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.MapBuilder; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.modules.core.ChoreographerCompat; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.uimanager.common.UIManagerType; @@ -20,7 +21,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -91,7 +91,8 @@ public int compare(Event lhs, Event rhs) { private final ArrayList mEventStaging = new ArrayList<>(); private final CopyOnWriteArrayList mListeners = new CopyOnWriteArrayList<>(); - private final List mPostEventDispatchListeners = new ArrayList<>(); + private final CopyOnWriteArrayList mPostEventDispatchListeners = + new CopyOnWriteArrayList<>(); private final ScheduleDispatchFrameCallback mCurrentFrameCallback = new ScheduleDispatchFrameCallback(); private final AtomicInteger mHasDispatchScheduledCount = new AtomicInteger(); @@ -263,6 +264,11 @@ public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitt mReactEventEmitter.register(uiManagerType, eventEmitter); } + public void registerEventEmitter( + @UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) { + mReactEventEmitter.register(uiManagerType, eventEmitter); + } + public void unregisterEventEmitter(@UIManagerType int uiManagerType) { mReactEventEmitter.unregister(uiManagerType); } @@ -361,7 +367,12 @@ public void run() { } Systrace.endAsyncFlow( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); - event.dispatch(mReactEventEmitter); + + if (ReactFeatureFlags.useDispatchUniqueForCoalescableEvents) { + event.dispatchModernV2(mReactEventEmitter); + } else { + event.dispatchModern(mReactEventEmitter); + } event.dispose(); } clearEventsToDispatch(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java new file mode 100644 index 0000000000000..a16f43494a27b --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/LockFreeEventDispatcherImpl.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager.events; + +import com.facebook.common.logging.FLog; +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.modules.core.ChoreographerCompat; +import com.facebook.react.modules.core.ReactChoreographer; +import com.facebook.react.uimanager.common.UIManagerType; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Class responsible for dispatching UI events to JS. The main purpose of this class is to act as an + * intermediary between UI code generating events and JS, making sure we don't send more events than + * JS can process. + * + *

To use it, create a subclass of {@link Event} and call {@link #dispatchEvent(Event)} whenever + * there's a UI event to dispatch. + * + *

This class works by installing a Choreographer frame callback on the main thread. This + * callback then enqueues a runnable on the JS thread (if one is not already pending) that is + * responsible for actually dispatch events to JS. This implementation depends on the properties + * that 1) FrameCallbacks run after UI events have been processed in Choreographer.java 2) when we + * enqueue a runnable on the JS queue thread, it won't be called until after any previously enqueued + * JS jobs have finished processing + * + *

If JS is taking a long time processing events, then the UI events generated on the UI thread + * can be coalesced into fewer events so that when the runnable runs, we don't overload JS with a + * ton of events and make it get even farther behind. + * + *

Ideally, we don't need this and JS is fast enough to process all the events each frame, but + * bad things happen, including load on CPUs from the system, and we should handle this case well. + * + *

== Event Cookies == + * + *

An event cookie is made up of the event type id, view tag, and a custom coalescing key. Only + * Events that have the same cookie can be coalesced. + * + *

Event Cookie Composition: VIEW_TAG_MASK = 0x00000000ffffffff EVENT_TYPE_ID_MASK = + * 0x0000ffff00000000 COALESCING_KEY_MASK = 0xffff000000000000 + * + *

This is a copy of EventDispatcherImpl, meant only to remove locking and synchronization. + */ +public class LockFreeEventDispatcherImpl implements EventDispatcher, LifecycleEventListener { + + private final boolean DEBUG_MODE = ReactBuildConfig.DEBUG; + private final String TAG = LockFreeEventDispatcherImpl.class.getSimpleName(); + + private final ReactApplicationContext mReactContext; + private final CopyOnWriteArrayList mListeners = + new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList mPostEventDispatchListeners = + new CopyOnWriteArrayList<>(); + private final LockFreeEventDispatcherImpl.ScheduleDispatchFrameCallback mCurrentFrameCallback = + new LockFreeEventDispatcherImpl.ScheduleDispatchFrameCallback(); + + private volatile ReactEventEmitter mReactEventEmitter; + + public LockFreeEventDispatcherImpl(ReactApplicationContext reactContext) { + mReactContext = reactContext; + mReactContext.addLifecycleEventListener(this); + mReactEventEmitter = new ReactEventEmitter(mReactContext); + } + + /** Sends the given Event to C++, where it will be flushed to JS ASAP. */ + public void dispatchEvent(Event event) { + Assertions.assertCondition(event.isInitialized(), "Dispatched event hasn't been initialized"); + Assertions.assertNotNull(mReactEventEmitter); + + if (DEBUG_MODE) { + FLog.v(TAG, "dispatchEvent: " + event.toString()); + } + + for (EventDispatcherListener listener : mListeners) { + listener.onEventDispatch(event); + } + + event.dispatchModernV2(mReactEventEmitter); + event.dispose(); + } + + public void dispatchAllEvents() { + maybePostFrameCallbackFromNonUI(); + } + + private void maybePostFrameCallbackFromNonUI() { + if (mReactEventEmitter != null) { + // If the host activity is paused, the frame callback may not be currently + // posted. Ensure that it is so that this event gets delivered promptly. + mCurrentFrameCallback.maybePostFromNonUI(); + } else { + // No JS application has started yet, or resumed. This can happen when a ReactRootView is + // added to view hierarchy, but ReactContext creation has not completed yet. In this case, any + // touch event dispatch will hit this codepath, and we simply queue them so that they + // are dispatched once ReactContext creation completes and JS app is running. + } + } + + /** Add a listener to this EventDispatcher. */ + public void addListener(EventDispatcherListener listener) { + mListeners.add(listener); + } + + /** Remove a listener from this EventDispatcher. */ + public void removeListener(EventDispatcherListener listener) { + mListeners.remove(listener); + } + + public void addBatchEventDispatchedListener(BatchEventDispatchedListener listener) { + mPostEventDispatchListeners.add(listener); + } + + public void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener) { + mPostEventDispatchListeners.remove(listener); + } + + @Override + public void onHostResume() { + maybePostFrameCallbackFromNonUI(); + } + + @Override + public void onHostPause() { + stopFrameCallback(); + } + + @Override + public void onHostDestroy() { + stopFrameCallback(); + } + + public void onCatalystInstanceDestroyed() { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + stopFrameCallback(); + } + }); + } + + private void stopFrameCallback() { + UiThreadUtil.assertOnUiThread(); + mCurrentFrameCallback.stop(); + } + + public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) { + mReactEventEmitter.register(uiManagerType, eventEmitter); + } + + public void registerEventEmitter( + @UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) { + mReactEventEmitter.register(uiManagerType, eventEmitter); + } + + public void unregisterEventEmitter(@UIManagerType int uiManagerType) { + mReactEventEmitter.unregister(uiManagerType); + } + + private class ScheduleDispatchFrameCallback extends ChoreographerCompat.FrameCallback { + private volatile boolean mIsPosted = false; + private boolean mShouldStop = false; + + @Override + public void doFrame(long frameTimeNanos) { + UiThreadUtil.assertOnUiThread(); + + if (mShouldStop) { + mIsPosted = false; + } else { + post(); + } + + driveEventBeats(); + } + + public void stop() { + mShouldStop = true; + } + + public void maybePost() { + if (!mIsPosted) { + mIsPosted = true; + post(); + } + } + + private void post() { + ReactChoreographer.getInstance() + .postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, mCurrentFrameCallback); + } + + public void maybePostFromNonUI() { + if (mIsPosted) { + return; + } + + // We should only hit this slow path when we receive events while the host activity is paused. + if (mReactContext.isOnUiQueueThread()) { + maybePost(); + } else { + mReactContext.runOnUiQueueThread( + new Runnable() { + @Override + public void run() { + maybePost(); + } + }); + } + } + } + + /** + * Is this a misnomer? It's called "driveEventBeats" but then is just calls + * `onBatchEventDispatched` listeners? Practically, all that `onBatchEventDispatched` is used for + * in Fabric is to drive AsyncEventBeats. + * + *

If this class ships for Fabric, we should remove `driveEventBeats` from here entirely, + * because it no longer needs to be connected to this event dispatcher. We should/probably can + * also delete `onRequestEventBeat` entirely. + */ + private void driveEventBeats() { + for (BatchEventDispatchedListener listener : mPostEventDispatchListeners) { + listener.onBatchEventDispatched(); + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java index da06d7257d545..06e7aa93664a0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java @@ -13,9 +13,26 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; +/** Deprecated in favor of RCTModernEventEmitter, which extends this interface. */ @DoNotStrip +@Deprecated public interface RCTEventEmitter extends JavaScriptModule { + /** + * Deprecated in favor of RCTModernEventEmitter.receiveEvent. + * + * @param targetTag + * @param eventName + * @param event + */ + @Deprecated void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event); + /** + * Receive and process touches + * + * @param eventName JS event name + * @param touches active pointers data + * @param changedIndices indices of changed pointers + */ void receiveTouches(String eventName, WritableArray touches, WritableArray changedIndices); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java new file mode 100644 index 0000000000000..4466e07b11c66 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uimanager.events; + +import androidx.annotation.Nullable; +import com.facebook.react.bridge.WritableMap; + +/** + * This is a transitional replacement for RCTEventEmitter that works with Fabric and non-Fabric + * renderers. RCTEventEmitter works with Fabric as well, but there are negative perf implications + * and it should be avoided. + * + *

This interface will *also* be deleted in the distant future and be replaced with a new + * interface that doesn't need the old `receiveEvent` method at all. But for the foreseeable future, + * this is the recommended interface to use for EventEmitters. + */ +public interface RCTModernEventEmitter extends RCTEventEmitter { + void receiveEvent(int surfaceId, int targetTag, String eventName, @Nullable WritableMap event); + + void receiveEvent( + int surfaceId, + int targetTag, + String eventName, + boolean canCoalesceEvent, + int customCoalesceKey, + @Nullable WritableMap event, + @EventCategoryDef int category); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java index 28931ad83cc2e..1ab5400106e1f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ReactEventEmitter.java @@ -9,23 +9,26 @@ import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY; -import android.util.SparseArray; import androidx.annotation.Nullable; -import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.common.ViewUtil; -public class ReactEventEmitter implements RCTEventEmitter { +public class ReactEventEmitter implements RCTModernEventEmitter { private static final String TAG = "ReactEventEmitter"; - private final SparseArray mEventEmitters = new SparseArray<>(); + @Nullable + private RCTModernEventEmitter mFabricEventEmitter = + null; /* Corresponds to a Fabric EventEmitter */ + + @Nullable + private RCTEventEmitter mRCTEventEmitter = null; /* Corresponds to a Non-Fabric EventEmitter */ private final ReactApplicationContext mReactContext; @@ -33,48 +36,71 @@ public ReactEventEmitter(ReactApplicationContext reactContext) { mReactContext = reactContext; } + public void register(@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) { + assert uiManagerType == UIManagerType.FABRIC; + mFabricEventEmitter = eventEmitter; + } + public void register(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) { - mEventEmitters.put(uiManagerType, eventEmitter); + assert uiManagerType == UIManagerType.DEFAULT; + mRCTEventEmitter = eventEmitter; } public void unregister(@UIManagerType int uiManagerType) { - mEventEmitters.remove(uiManagerType); + if (uiManagerType == UIManagerType.DEFAULT) { + mRCTEventEmitter = null; + } else { + mFabricEventEmitter = null; + } } @Override public void receiveEvent(int targetReactTag, String eventName, @Nullable WritableMap event) { - RCTEventEmitter eventEmitter = getEventEmitter(targetReactTag); - if (eventEmitter != null) { - eventEmitter.receiveEvent(targetReactTag, eventName, event); - } + receiveEvent(-1, targetReactTag, eventName, event); + } + + @Override + public void receiveEvent( + int surfaceId, int targetTag, String eventName, @Nullable WritableMap event) { + // The two additional params here, `canCoalesceEvent` and `customCoalesceKey`, have no + // meaning outside of Fabric. + receiveEvent(surfaceId, targetTag, eventName, false, 0, event, EventCategoryDef.UNSPECIFIED); } @Override public void receiveTouches( String eventName, WritableArray touches, WritableArray changedIndices) { - Assertions.assertCondition(touches.size() > 0); int reactTag = touches.getMap(0).getInt(TARGET_KEY); - RCTEventEmitter eventEmitter = getEventEmitter(reactTag); - if (eventEmitter != null) { - eventEmitter.receiveTouches(eventName, touches, changedIndices); + @UIManagerType int uiManagerType = ViewUtil.getUIManagerType(reactTag); + if (uiManagerType == UIManagerType.FABRIC && mFabricEventEmitter != null) { + mFabricEventEmitter.receiveTouches(eventName, touches, changedIndices); + } else if (uiManagerType == UIManagerType.DEFAULT && getEventEmitter(reactTag) != null) { + mRCTEventEmitter.receiveTouches(eventName, touches, changedIndices); + } else { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Cannot find EventEmitter for receivedTouches: ReactTag[" + + reactTag + + "] UIManagerType[" + + uiManagerType + + "] EventName[" + + eventName + + "]")); } } @Nullable private RCTEventEmitter getEventEmitter(int reactTag) { int type = ViewUtil.getUIManagerType(reactTag); - RCTEventEmitter eventEmitter = mEventEmitters.get(type); - if (eventEmitter == null) { - // TODO T54145494: Refactor RN Event Emitter system to make sure reactTags are always managed - // by RN - FLog.e( - TAG, "Unable to find event emitter for reactTag: %d - uiManagerType: %d", reactTag, type); - if (mReactContext.hasActiveCatalystInstance()) { - eventEmitter = mReactContext.getJSModule(RCTEventEmitter.class); + assert type == UIManagerType.DEFAULT; + if (mRCTEventEmitter == null) { + if (mReactContext.hasActiveReactInstance()) { + mRCTEventEmitter = mReactContext.getJSModule(RCTEventEmitter.class); } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( TAG, new ReactNoCrashSoftException( "Cannot get RCTEventEmitter from Context for reactTag: " @@ -84,6 +110,43 @@ private RCTEventEmitter getEventEmitter(int reactTag) { + " - No active Catalyst instance!")); } } - return eventEmitter; + return mRCTEventEmitter; + } + + @Override + public void receiveEvent( + int surfaceId, + int targetReactTag, + String eventName, + boolean canCoalesceEvent, + int customCoalesceKey, + @Nullable WritableMap event, + @EventCategoryDef int category) { + @UIManagerType int uiManagerType = ViewUtil.getUIManagerType(targetReactTag); + if (uiManagerType == UIManagerType.FABRIC && mFabricEventEmitter != null) { + mFabricEventEmitter.receiveEvent( + surfaceId, + targetReactTag, + eventName, + canCoalesceEvent, + customCoalesceKey, + event, + category); + } else if (uiManagerType == UIManagerType.DEFAULT && getEventEmitter(targetReactTag) != null) { + mRCTEventEmitter.receiveEvent(targetReactTag, eventName, event); + } else { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Cannot find EventEmitter for receiveEvent: SurfaceId[" + + surfaceId + + "] ReactTag[" + + targetReactTag + + "] UIManagerType[" + + uiManagerType + + "] EventName[" + + eventName + + "]")); + } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java index 1445735f6f57f..eb8b7a78a7e5f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.java @@ -11,7 +11,9 @@ import androidx.annotation.Nullable; import androidx.core.util.Pools; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.SoftAssertions; +import com.facebook.react.config.ReactFeatureFlags; /** * An event representing the start, end or movement of a touch. Corresponds to a single {@link @@ -22,6 +24,7 @@ * these coalescing keys are determined. */ public class TouchEvent extends Event { + private static final String TAG = TouchEvent.class.getSimpleName(); private static final int TOUCH_EVENTS_POOL_SIZE = 3; @@ -30,6 +33,7 @@ public class TouchEvent extends Event { public static final long UNSET = Long.MIN_VALUE; + @Deprecated public static TouchEvent obtain( int viewTag, TouchEventType touchEventType, @@ -38,14 +42,35 @@ public static TouchEvent obtain( float viewX, float viewY, TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) { + return obtain( + -1, + viewTag, + touchEventType, + Assertions.assertNotNull(motionEventToCopy), + gestureStartTime, + viewX, + viewY, + touchEventCoalescingKeyHelper); + } + + public static TouchEvent obtain( + int surfaceId, + int viewTag, + TouchEventType touchEventType, + MotionEvent motionEventToCopy, + long gestureStartTime, + float viewX, + float viewY, + TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) { TouchEvent event = EVENTS_POOL.acquire(); if (event == null) { event = new TouchEvent(); } event.init( + surfaceId, viewTag, touchEventType, - motionEventToCopy, + Assertions.assertNotNull(motionEventToCopy), gestureStartTime, viewX, viewY, @@ -64,6 +89,7 @@ public static TouchEvent obtain( private TouchEvent() {} private void init( + int surfaceId, int viewTag, TouchEventType touchEventType, MotionEvent motionEventToCopy, @@ -71,7 +97,7 @@ private void init( float viewX, float viewY, TouchEventCoalescingKeyHelper touchEventCoalescingKeyHelper) { - super.init(viewTag); + super.init(surfaceId, viewTag, motionEventToCopy.getEventTime()); SoftAssertions.assertCondition( gestureStartTime != UNSET, "Gesture start time must be initialized"); @@ -106,9 +132,25 @@ private void init( @Override public void onDispose() { - Assertions.assertNotNull(mMotionEvent).recycle(); + MotionEvent motionEvent = mMotionEvent; mMotionEvent = null; - EVENTS_POOL.release(this); + if (motionEvent != null) { + motionEvent.recycle(); + } + + // Either `this` is in the event pool, or motionEvent + // is null. It is in theory not possible for a TouchEvent to + // be in the EVENTS_POOL but for motionEvent to be null. However, + // out of an abundance of caution and to avoid memory leaks or + // other crashes at all costs, we attempt to release here and log + // a soft exception here if release throws an IllegalStateException + // due to `this` being over-released. This may indicate that there is + // a logic error in our events system or pooling mechanism. + try { + EVENTS_POOL.release(this); + } catch (IllegalStateException e) { + ReactSoftExceptionLogger.logSoftException(TAG, e); + } } @Override @@ -140,8 +182,54 @@ public short getCoalescingKey() { @Override public void dispatch(RCTEventEmitter rctEventEmitter) { - TouchesHelper.sendTouchEvent( - rctEventEmitter, Assertions.assertNotNull(mTouchEventType), getViewTag(), this); + if (!hasMotionEvent()) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Cannot dispatch a TouchEvent that has no MotionEvent; the TouchEvent has been recycled")); + return; + } + + TouchesHelper.sendTouchEvent(rctEventEmitter, this); + } + + @Override + public void dispatchModern(RCTModernEventEmitter rctEventEmitter) { + if (ReactFeatureFlags.useUpdatedTouchPreprocessing) { + TouchesHelper.sendTouchEventModern(rctEventEmitter, this, /* useDispatchV2 */ false); + } else { + dispatch(rctEventEmitter); + } + } + + @Override + public void dispatchModernV2(RCTModernEventEmitter rctEventEmitter) { + if (ReactFeatureFlags.useUpdatedTouchPreprocessing) { + TouchesHelper.sendTouchEventModern(rctEventEmitter, this, /* useDispatchV2 */ true); + } else { + dispatch(rctEventEmitter); + } + } + + @Override + protected int getEventCategory() { + TouchEventType type = mTouchEventType; + if (type == null) { + return EventCategoryDef.UNSPECIFIED; + } + + switch (type) { + case START: + return EventCategoryDef.CONTINUOUS_START; + case END: + case CANCEL: + return EventCategoryDef.CONTINUOUS_END; + case MOVE: + return EventCategoryDef.CONTINUOUS; + } + + // Something something smart compiler... + return super.getEventCategory(); } public MotionEvent getMotionEvent() { @@ -149,6 +237,14 @@ public MotionEvent getMotionEvent() { return mMotionEvent; } + private boolean hasMotionEvent() { + return mMotionEvent != null; + } + + public TouchEventType getTouchEventType() { + return Assertions.assertNotNull(mTouchEventType); + } + public float getViewX() { return mViewX; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java index f7dd5b1c7926d..9a60a8c7197bc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEventType.java @@ -9,23 +9,22 @@ /** Touch event types that JS module RCTEventEmitter can understand */ public enum TouchEventType { - START, - END, - MOVE, - CANCEL; + START("topTouchStart"), + END("topTouchEnd"), + MOVE("topTouchMove"), + CANCEL("topTouchCancel"); + + private final String mJsName; + + TouchEventType(String jsName) { + mJsName = jsName; + } + + public String getJsName() { + return mJsName; + } public static String getJSEventName(TouchEventType type) { - switch (type) { - case START: - return "topTouchStart"; - case END: - return "topTouchEnd"; - case MOVE: - return "topTouchMove"; - case CANCEL: - return "topTouchCancel"; - default: - throw new IllegalArgumentException("Unexpected type " + type); - } + return type.getJsName(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java index 0c222ad95022c..9b60f6f8030fc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.java @@ -9,18 +9,17 @@ import android.view.MotionEvent; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.PixelUtil; /** Class responsible for generating catalyst touch events based on android {@link MotionEvent}. */ public class TouchesHelper { - + public static final String TARGET_SURFACE_KEY = "targetSurface"; public static final String TARGET_KEY = "target"; public static final String CHANGED_TOUCHES_KEY = "changedTouches"; public static final String TOUCHES_KEY = "touches"; - public static final String TOP_TOUCH_END_KEY = "topTouchEnd"; - public static final String TOP_TOUCH_CANCEL_KEY = "topTouchCancel"; private static final String PAGE_X_KEY = "pageX"; private static final String PAGE_Y_KEY = "pageY"; private static final String TIMESTAMP_KEY = "timestamp"; @@ -29,14 +28,16 @@ public class TouchesHelper { private static final String LOCATION_X_KEY = "locationX"; private static final String LOCATION_Y_KEY = "locationY"; + private static final String TAG = "TouchesHelper"; + /** * Creates catalyst pointers array in format that is expected by RCTEventEmitter JS module from * given {@param event} instance. This method use {@param reactTarget} parameter to set as a * target view id associated with current gesture. */ - private static WritableArray createsPointersArray(int reactTarget, TouchEvent event) { - WritableArray touches = Arguments.createArray(); + private static WritableMap[] createPointersArray(TouchEvent event) { MotionEvent motionEvent = event.getMotionEvent(); + WritableMap[] touches = new WritableMap[motionEvent.getPointerCount()]; // Calculate the coordinates for the target view. // The MotionEvent contains the X,Y of the touch in the coordinate space of the root view @@ -60,10 +61,12 @@ private static WritableArray createsPointersArray(int reactTarget, TouchEvent ev float locationY = motionEvent.getY(index) - targetViewCoordinateY; touch.putDouble(LOCATION_X_KEY, PixelUtil.toDIPFromPixel(locationX)); touch.putDouble(LOCATION_Y_KEY, PixelUtil.toDIPFromPixel(locationY)); - touch.putInt(TARGET_KEY, reactTarget); + touch.putInt(TARGET_SURFACE_KEY, event.getSurfaceId()); + touch.putInt(TARGET_KEY, event.getViewTag()); touch.putDouble(TIMESTAMP_KEY, event.getTimestampMs()); touch.putDouble(POINTER_IDENTIFIER_KEY, motionEvent.getPointerId(index)); - touches.pushMap(touch); + + touches[index] = touch; } return touches; @@ -74,17 +77,12 @@ private static WritableArray createsPointersArray(int reactTarget, TouchEvent ev * context}. Touch event can encode multiple concurrent touches (pointers). * * @param rctEventEmitter Event emitter used to execute JS module call - * @param type type of the touch event (see {@link TouchEventType}) - * @param reactTarget target view react id associated with this gesture * @param touchEvent native touch event to read pointers count and coordinates from */ - public static void sendTouchEvent( - RCTEventEmitter rctEventEmitter, - TouchEventType type, - int reactTarget, - TouchEvent touchEvent) { + public static void sendTouchEvent(RCTEventEmitter rctEventEmitter, TouchEvent touchEvent) { + TouchEventType type = touchEvent.getTouchEventType(); - WritableArray pointers = createsPointersArray(reactTarget, touchEvent); + WritableArray pointers = getWritableArray(createPointersArray(touchEvent)); MotionEvent motionEvent = touchEvent.getMotionEvent(); // For START and END events send only index of the pointer that is associated with that event @@ -102,4 +100,98 @@ public static void sendTouchEvent( rctEventEmitter.receiveTouches(TouchEventType.getJSEventName(type), pointers, changedIndices); } + + /** + * Generate touch event data to match JS expectations. Combines logic in {@link #sendTouchEvent} + * and FabricEventEmitter to create the same data structure in a more efficient manner. + * + *

Touches have to be dispatched as separate events for each changed pointer to make JS process + * them correctly. To avoid allocations, we preprocess touch events in Java world and then convert + * them to native before dispatch. + * + * @param eventEmitter emitter to dispatch event to + * @param event the touch event to extract data from + * @param useDispatchV2 whether to dispatch additional data used by {@link Event#dispatchModernV2} + */ + public static void sendTouchEventModern( + RCTModernEventEmitter eventEmitter, TouchEvent event, boolean useDispatchV2) { + TouchEventType type = event.getTouchEventType(); + MotionEvent motionEvent = event.getMotionEvent(); + + if (motionEvent == null) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException( + "Cannot dispatch a TouchEvent that has no MotionEvent; the TouchEvent has been recycled")); + return; + } + + WritableMap[] touches = createPointersArray(event); + WritableMap[] changedTouches = null; + + switch (type) { + case START: + int newPointerIndex = motionEvent.getActionIndex(); + + changedTouches = new WritableMap[] {touches[newPointerIndex].copy()}; + break; + case END: + int finishedPointerIndex = motionEvent.getActionIndex(); + /* + * Clear finished pointer index for compatibility with W3C touch "end" events, where the + * active touches don't include the set that has just been "ended". + */ + WritableMap finishedPointer = touches[finishedPointerIndex]; + touches[finishedPointerIndex] = null; + + changedTouches = new WritableMap[] {finishedPointer}; + break; + case MOVE: + changedTouches = new WritableMap[touches.length]; + for (int i = 0; i < touches.length; i++) { + changedTouches[i] = touches[i].copy(); + } + break; + case CANCEL: + changedTouches = touches; + touches = new WritableMap[0]; + break; + } + + WritableArray touchesArray = getWritableArray(touches); + WritableArray changedTouchesArray = getWritableArray(/* copyObjects */ true, changedTouches); + + for (WritableMap eventData : changedTouches) { + eventData.putArray(CHANGED_TOUCHES_KEY, changedTouchesArray); + eventData.putArray(TOUCHES_KEY, touchesArray); + + if (useDispatchV2) { + eventEmitter.receiveEvent( + event.getSurfaceId(), + event.getViewTag(), + event.getEventName(), + event.canCoalesce(), + 0, + eventData, + event.getEventCategory()); + } else { + eventEmitter.receiveEvent( + event.getSurfaceId(), event.getViewTag(), event.getEventName(), eventData); + } + } + } + + private static WritableArray getWritableArray(WritableMap... objects) { + return getWritableArray(false, objects); + } + + private static WritableArray getWritableArray(boolean copyObjects, WritableMap... objects) { + WritableArray result = Arguments.createArray(); + for (WritableMap object : objects) { + if (object != null) { + result.pushMap(copyObjects ? object.copy() : object); + } + } + return result; + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BUCK similarity index 56% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BUCK index 039099fb0d634..8922b82d73d75 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BUCK @@ -1,18 +1,21 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") rn_android_library( - name = "viewmanagers", + name = "interfaces", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), ], + required_for_source_only_abi = True, visibility = [ "PUBLIC", ], deps = [ + YOGA_TARGET, + react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/uimanager:uimanager"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BaseViewManagerDelegate.java similarity index 100% rename from android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BaseViewManagerDelegate.java diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BaseViewManagerInterface.java similarity index 100% rename from android/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.java rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BaseViewManagerInterface.java diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/FloatUtil.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/FloatUtil.java similarity index 100% rename from android/ReactAndroid/src/main/java/com/facebook/react/uimanager/FloatUtil.java rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/FloatUtil.java diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/Spacing.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/Spacing.java similarity index 100% rename from android/ReactAndroid/src/main/java/com/facebook/react/uimanager/Spacing.java rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/Spacing.java diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewManagerDelegate.java similarity index 100% rename from android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.java rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewManagerDelegate.java diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java similarity index 96% rename from android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java rename to android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java index ac780e8bb4f91..61151919cc0db 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java @@ -9,6 +9,7 @@ import android.graphics.Color; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableType; import java.util.Arrays; import java.util.HashSet; @@ -81,6 +82,7 @@ public class ViewProps { // Props that affect more than just layout public static final String ENABLED = "enabled"; public static final String BACKGROUND_COLOR = "backgroundColor"; + public static final String FOREGROUND_COLOR = "foregroundColor"; public static final String COLOR = "color"; public static final String FONT_SIZE = "fontSize"; public static final String FONT_WEIGHT = "fontWeight"; @@ -257,8 +259,14 @@ public static boolean isLayoutOnly(ReadableMap map, String prop) { // Ignore if explicitly set to default opacity. return map.isNull(OPACITY) || map.getDouble(OPACITY) == 1d; case BORDER_RADIUS: // Without a background color or border width set, a border won't show. - if (map.hasKey(BACKGROUND_COLOR) && map.getInt(BACKGROUND_COLOR) != Color.TRANSPARENT) { - return false; + if (map.hasKey(BACKGROUND_COLOR)) { + ReadableType valueType = map.getType(BACKGROUND_COLOR); + if (valueType == ReadableType.Number + && map.getInt(BACKGROUND_COLOR) != Color.TRANSPARENT) { + return false; + } else if (valueType != ReadableType.Null) { + return false; + } } if (map.hasKey(BORDER_WIDTH) && !map.isNull(BORDER_WIDTH) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/Android.mk new file mode 100644 index 0000000000000..a4085b466b0fc --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/Android.mk @@ -0,0 +1,37 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := uimanagerjni + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_SHARED_LIBRARIES := libyoga libglog libfb libfbjni libglog_init libfolly_json libfolly_futures react_render_componentregistry librrc_native + +LOCAL_STATIC_LIBRARIES := + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"ReacTNative\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,fbgloginit) +$(call import-module,folly) +$(call import-module,fb) +$(call import-module,fbjni) +$(call import-module,yogajni) +$(call import-module,glog) + +$(call import-module,react/renderer/componentregistry) +$(call import-module,react/renderer/componentregistry/native) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/BUCK new file mode 100644 index 0000000000000..5ded79192300c --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/BUCK @@ -0,0 +1,35 @@ +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob") + +rn_xplat_cxx_library( + name = "jni", + srcs = glob(["*.cpp"]), + headers = glob(["*.h"]), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "**/*.h"), + ], + prefix = "react/uimanager/jni", + ), + fbandroid_allow_jni_merging = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + platforms = ANDROID, + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + soname = "libuimanagerjni.$(ext)", + visibility = ["PUBLIC"], + deps = [ + react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + react_native_xplat_target("react/renderer/componentregistry:componentregistry"), + react_native_xplat_target("react/renderer/componentregistry/native:native"), + react_native_target("jni/react/jni:jni"), + "//xplat/fbsystrace:fbsystrace", + "//xplat/folly:molly", + "//xplat/jsi:JSIDynamic", + "//xplat/jsi:jsi", + "//xplat/third-party/linker_lib:atomic", + FBJNI_TARGET, + ], +) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.cpp new file mode 100644 index 0000000000000..cbd69e2f4912d --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +#include "ComponentNameResolverManager.h" + +#include + +namespace facebook { +namespace react { + +using namespace facebook::jni; + +ComponentNameResolverManager::ComponentNameResolverManager( + jni::alias_ref jThis, + RuntimeExecutor runtimeExecutor, + jni::alias_ref componentNameResolver) + : javaPart_(jni::make_global(jThis)), + runtimeExecutor_(runtimeExecutor), + componentNameResolver_(jni::make_global(componentNameResolver)) {} + +jni::local_ref +ComponentNameResolverManager::initHybrid( + jni::alias_ref jThis, + jni::alias_ref runtimeExecutor, + jni::alias_ref componentNameResolver) { + return makeCxxInstance( + jThis, runtimeExecutor->cthis()->get(), componentNameResolver); +} + +void ComponentNameResolverManager::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", ComponentNameResolverManager::initHybrid), + makeNativeMethod( + "installJSIBindings", + ComponentNameResolverManager::installJSIBindings), + }); +} + +void ComponentNameResolverManager::installJSIBindings() { + runtimeExecutor_([thizz = this](jsi::Runtime &runtime) { + auto viewManagerProvider = [thizz](const std::string &name) -> bool { + if (thizz->componentNames_.size() == 0) { + static auto getComponentNames = + jni::findClassStatic(ComponentNameResolverManager:: + ComponentNameResolverJavaDescriptor) + ->getMethod>()>( + "getComponentNames"); + + auto componentNamesJArray = + getComponentNames(thizz->componentNameResolver_.get()); + auto len = componentNamesJArray->size(); + for (size_t i = 0; i < len; i++) { + jni::local_ref elem = (*componentNamesJArray)[i]; + auto componentName = elem->toStdString(); + thizz->componentNames_.insert(componentName); + } + } + + return thizz->componentNames_.find(name) != thizz->componentNames_.end(); + }; + + react::NativeComponentRegistryBinding::install( + runtime, std::move(viewManagerProvider)); + }); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.h b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.h new file mode 100644 index 0000000000000..e8a1285831713 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/ComponentNameResolverManager.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class ComponentNameResolverManager + : public facebook::jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = + "Lcom/facebook/react/uimanager/ComponentNameResolverManager;"; + + constexpr static auto ComponentNameResolverJavaDescriptor = + "com/facebook/react/uimanager/ComponentNameResolver"; + + static facebook::jni::local_ref initHybrid( + facebook::jni::alias_ref jThis, + facebook::jni::alias_ref runtimeExecutor, + facebook::jni::alias_ref componentNameResolver); + + static void registerNatives(); + + private: + friend HybridBase; + facebook::jni::global_ref javaPart_; + RuntimeExecutor runtimeExecutor_; + + facebook::jni::global_ref componentNameResolver_; + + std::set componentNames_; + + void installJSIBindings(); + + explicit ComponentNameResolverManager( + facebook::jni::alias_ref + jThis, + RuntimeExecutor runtimeExecutor, + facebook::jni::alias_ref componentNameResolver); +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/OnLoad.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/OnLoad.cpp new file mode 100644 index 0000000000000..b7167dfbd9cce --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/jni/OnLoad.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include "ComponentNameResolverManager.h" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize(vm, [] { + facebook::react::ComponentNameResolverManager::registerNatives(); + }); +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK index 6d31563ef3a53..c8173be0b382a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "util", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/util/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/util/BUCK index 679245155e108..67fc95476d595 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/util/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/util/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "util", srcs = glob(["**/*.java"]), + autoglob = False, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerDelegate.java deleted file mode 100644 index 833ffe265d96f..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerDelegate.java +++ /dev/null @@ -1,42 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class ActivityIndicatorViewManagerDelegate & ActivityIndicatorViewManagerInterface> extends BaseViewManagerDelegate { - public ActivityIndicatorViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "hidesWhenStopped": - mViewManager.setHidesWhenStopped(view, value == null ? false : (boolean) value); - break; - case "animating": - mViewManager.setAnimating(view, value == null ? false : (boolean) value); - break; - case "color": - mViewManager.setColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "size": - mViewManager.setSize(view, (String) value); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerInterface.java deleted file mode 100644 index d217b8bc2fdfb..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ActivityIndicatorViewManagerInterface.java +++ /dev/null @@ -1,20 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface ActivityIndicatorViewManagerInterface { - void setHidesWhenStopped(T view, boolean value); - void setAnimating(T view, boolean value); - void setColor(T view, @Nullable Integer value); - void setSize(T view, @Nullable String value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerDelegate.java deleted file mode 100644 index 8147f55a3d511..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerDelegate.java +++ /dev/null @@ -1,48 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidDialogPickerManagerDelegate & AndroidDialogPickerManagerInterface> extends BaseViewManagerDelegate { - public AndroidDialogPickerManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "color": - mViewManager.setColor(view, value == null ? null : ((Double) value).intValue()); - break; - case "backgroundColor": - mViewManager.setBackgroundColor(view, value == null ? null : ((Double) value).intValue()); - break; - case "enabled": - mViewManager.setEnabled(view, value == null ? true : (boolean) value); - break; - case "items": - mViewManager.setItems(view, (ReadableArray) value); - break; - case "prompt": - mViewManager.setPrompt(view, value == null ? "" : (String) value); - break; - case "selected": - mViewManager.setSelected(view, value == null ? 0 : ((Double) value).intValue()); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerInterface.java deleted file mode 100644 index fe8986fa6cbaf..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDialogPickerManagerInterface.java +++ /dev/null @@ -1,23 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; - -public interface AndroidDialogPickerManagerInterface { - void setColor(T view, @Nullable Integer value); - void setBackgroundColor(T view, @Nullable Integer value); - void setEnabled(T view, boolean value); - void setItems(T view, @Nullable ReadableArray value); - void setPrompt(T view, @Nullable String value); - void setSelected(T view, int value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java deleted file mode 100644 index aee5f3f9c9482..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java +++ /dev/null @@ -1,61 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidDrawerLayoutManagerDelegate & AndroidDrawerLayoutManagerInterface> extends BaseViewManagerDelegate { - public AndroidDrawerLayoutManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "keyboardDismissMode": - mViewManager.setKeyboardDismissMode(view, (String) value); - break; - case "drawerBackgroundColor": - mViewManager.setDrawerBackgroundColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "drawerPosition": - mViewManager.setDrawerPosition(view, (String) value); - break; - case "drawerWidth": - mViewManager.setDrawerWidth(view, value == null ? null : ((Double) value).floatValue()); - break; - case "drawerLockMode": - mViewManager.setDrawerLockMode(view, (String) value); - break; - case "statusBarBackgroundColor": - mViewManager.setStatusBarBackgroundColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - default: - super.setProperty(view, propName, value); - } - } - - @Override - public void receiveCommand(T view, String commandName, ReadableArray args) { - switch (commandName) { - case "openDrawer": - mViewManager.openDrawer(view); - break; - case "closeDrawer": - mViewManager.closeDrawer(view); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface.java deleted file mode 100644 index 7654b2d79297a..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface.java +++ /dev/null @@ -1,24 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface AndroidDrawerLayoutManagerInterface { - void setKeyboardDismissMode(T view, @Nullable String value); - void setDrawerBackgroundColor(T view, @Nullable Integer value); - void setDrawerPosition(T view, @Nullable String value); - void setDrawerWidth(T view, @Nullable Float value); - void setDrawerLockMode(T view, @Nullable String value); - void setStatusBarBackgroundColor(T view, @Nullable Integer value); - void openDrawer(T view); - void closeDrawer(T view); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerDelegate.java deleted file mode 100644 index c14ce248b59fa..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerDelegate.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - *

This source code is licensed under the MIT license found in the LICENSE file in the root - * directory of this source tree. - * - * @generated by codegen project: GeneratePropsJavaDelegate.js - */ -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidDropdownPickerManagerDelegate< - T extends View, - U extends BaseViewManagerInterface & AndroidDropdownPickerManagerInterface> - extends BaseViewManagerDelegate { - public AndroidDropdownPickerManagerDelegate(U viewManager) { - super(viewManager); - } - - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "color": - mViewManager.setColor(view, value == null ? null : ((Double) value).intValue()); - break; - case "enabled": - mViewManager.setEnabled(view, value == null ? true : (boolean) value); - break; - case "items": - mViewManager.setItems(view, (ReadableArray) value); - break; - case "prompt": - mViewManager.setPrompt(view, value == null ? "" : (String) value); - break; - case "selected": - mViewManager.setSelected(view, value == null ? 0 : ((Double) value).intValue()); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerInterface.java deleted file mode 100644 index db599a7905644..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDropdownPickerManagerInterface.java +++ /dev/null @@ -1,22 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; - -public interface AndroidDropdownPickerManagerInterface { - void setColor(T view, @Nullable Integer value); - void setEnabled(T view, boolean value); - void setItems(T view, @Nullable ReadableArray value); - void setPrompt(T view, @Nullable String value); - void setSelected(T view, int value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerDelegate.java deleted file mode 100644 index b0417eed9ce04..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerDelegate.java +++ /dev/null @@ -1,51 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidProgressBarManagerDelegate & AndroidProgressBarManagerInterface> extends BaseViewManagerDelegate { - public AndroidProgressBarManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "styleAttr": - mViewManager.setStyleAttr(view, value == null ? null : (String) value); - break; - case "typeAttr": - mViewManager.setTypeAttr(view, value == null ? null : (String) value); - break; - case "indeterminate": - mViewManager.setIndeterminate(view, value == null ? false : (boolean) value); - break; - case "progress": - mViewManager.setProgress(view, value == null ? 0f : ((Double) value).doubleValue()); - break; - case "animating": - mViewManager.setAnimating(view, value == null ? true : (boolean) value); - break; - case "color": - mViewManager.setColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "testID": - mViewManager.setTestID(view, value == null ? "" : (String) value); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerInterface.java deleted file mode 100644 index 814d8e7f7e14c..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidProgressBarManagerInterface.java +++ /dev/null @@ -1,23 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface AndroidProgressBarManagerInterface { - void setStyleAttr(T view, @Nullable String value); - void setTypeAttr(T view, @Nullable String value); - void setIndeterminate(T view, boolean value); - void setProgress(T view, double value); - void setAnimating(T view, boolean value); - void setColor(T view, @Nullable Integer value); - void setTestID(T view, @Nullable String value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java deleted file mode 100644 index e5fc3ac7a2c2c..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java +++ /dev/null @@ -1,58 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidSwipeRefreshLayoutManagerDelegate & AndroidSwipeRefreshLayoutManagerInterface> extends BaseViewManagerDelegate { - public AndroidSwipeRefreshLayoutManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "enabled": - mViewManager.setEnabled(view, value == null ? true : (boolean) value); - break; - case "colors": - mViewManager.setColors(view, (ReadableArray) value); - break; - case "progressBackgroundColor": - mViewManager.setProgressBackgroundColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "size": - mViewManager.setSize(view, value == null ? 1 : ((Double) value).intValue()); - break; - case "progressViewOffset": - mViewManager.setProgressViewOffset(view, value == null ? 0f : ((Double) value).floatValue()); - break; - case "refreshing": - mViewManager.setRefreshing(view, value == null ? false : (boolean) value); - break; - default: - super.setProperty(view, propName, value); - } - } - - @Override - public void receiveCommand(T view, String commandName, ReadableArray args) { - switch (commandName) { - case "setNativeRefreshing": - mViewManager.setNativeRefreshing(view, args.getBoolean(0)); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerInterface.java deleted file mode 100644 index 3e81326b3e8ee..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerInterface.java +++ /dev/null @@ -1,24 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; - -public interface AndroidSwipeRefreshLayoutManagerInterface { - void setEnabled(T view, boolean value); - void setColors(T view, @Nullable ReadableArray value); - void setProgressBackgroundColor(T view, @Nullable Integer value); - void setSize(T view, int value); - void setProgressViewOffset(T view, float value); - void setRefreshing(T view, boolean value); - void setNativeRefreshing(T view, boolean value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java deleted file mode 100644 index 1b2f225d390ae..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java +++ /dev/null @@ -1,67 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidSwitchManagerDelegate & AndroidSwitchManagerInterface> extends BaseViewManagerDelegate { - public AndroidSwitchManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "disabled": - mViewManager.setDisabled(view, value == null ? false : (boolean) value); - break; - case "enabled": - mViewManager.setEnabled(view, value == null ? true : (boolean) value); - break; - case "thumbColor": - mViewManager.setThumbColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackColorForFalse": - mViewManager.setTrackColorForFalse(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackColorForTrue": - mViewManager.setTrackColorForTrue(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "value": - mViewManager.setValue(view, value == null ? false : (boolean) value); - break; - case "on": - mViewManager.setOn(view, value == null ? false : (boolean) value); - break; - case "thumbTintColor": - mViewManager.setThumbTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackTintColor": - mViewManager.setTrackTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - default: - super.setProperty(view, propName, value); - } - } - - @Override - public void receiveCommand(T view, String commandName, ReadableArray args) { - switch (commandName) { - case "setNativeValue": - mViewManager.setNativeValue(view, args.getBoolean(0)); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerInterface.java deleted file mode 100644 index 2ff293b3be663..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerInterface.java +++ /dev/null @@ -1,26 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface AndroidSwitchManagerInterface { - void setDisabled(T view, boolean value); - void setEnabled(T view, boolean value); - void setThumbColor(T view, @Nullable Integer value); - void setTrackColorForFalse(T view, @Nullable Integer value); - void setTrackColorForTrue(T view, @Nullable Integer value); - void setValue(T view, boolean value); - void setOn(T view, boolean value); - void setThumbTintColor(T view, @Nullable Integer value); - void setTrackTintColor(T view, @Nullable Integer value); - void setNativeValue(T view, boolean value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java deleted file mode 100644 index 68c110b32412e..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java +++ /dev/null @@ -1,57 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class AndroidViewPagerManagerDelegate & AndroidViewPagerManagerInterface> extends BaseViewManagerDelegate { - public AndroidViewPagerManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "initialPage": - mViewManager.setInitialPage(view, value == null ? 0 : ((Double) value).intValue()); - break; - case "pageMargin": - mViewManager.setPageMargin(view, value == null ? 0 : ((Double) value).intValue()); - break; - case "peekEnabled": - mViewManager.setPeekEnabled(view, value == null ? false : (boolean) value); - break; - case "keyboardDismissMode": - mViewManager.setKeyboardDismissMode(view, (String) value); - break; - case "scrollEnabled": - mViewManager.setScrollEnabled(view, value == null ? true : (boolean) value); - break; - default: - super.setProperty(view, propName, value); - } - } - - @Override - public void receiveCommand(T view, String commandName, ReadableArray args) { - switch (commandName) { - case "setPage": - mViewManager.setPage(view, args.getInt(0)); - break; - case "setPageWithoutAnimation": - mViewManager.setPageWithoutAnimation(view, args.getInt(0)); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerInterface.java deleted file mode 100644 index 2f9e892400ee2..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerInterface.java +++ /dev/null @@ -1,23 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface AndroidViewPagerManagerInterface { - void setInitialPage(T view, int value); - void setPageMargin(T view, int value); - void setPeekEnabled(T view, boolean value); - void setKeyboardDismissMode(T view, @Nullable String value); - void setScrollEnabled(T view, boolean value); - void setPage(T view, int page); - void setPageWithoutAnimation(T view, int page); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerDelegate.java deleted file mode 100644 index 29288a63c8911..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerDelegate.java +++ /dev/null @@ -1,62 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class DatePickerManagerDelegate & DatePickerManagerInterface> extends BaseViewManagerDelegate { - public DatePickerManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "date": - mViewManager.setDate(view, value == null ? 0f : ((Double) value).floatValue()); - break; - case "initialDate": - mViewManager.setInitialDate(view, value == null ? 0f : ((Double) value).floatValue()); - break; - case "locale": - mViewManager.setLocale(view, value == null ? null : (String) value); - break; - case "maximumDate": - mViewManager.setMaximumDate(view, value == null ? 0f : ((Double) value).floatValue()); - break; - case "minimumDate": - mViewManager.setMinimumDate(view, value == null ? 0f : ((Double) value).floatValue()); - break; - case "minuteInterval": - mViewManager.setMinuteInterval(view, value == null ? 1 : ((Double) value).intValue()); - break; - case "mode": - mViewManager.setMode(view, (String) value); - break; - case "timeZoneOffsetInMinutes": - mViewManager.setTimeZoneOffsetInMinutes(view, value == null ? 0f : ((Double) value).floatValue()); - break; - default: - super.setProperty(view, propName, value); - } - } - - public void receiveCommand(DatePickerManagerInterface viewManager, T view, String commandName, ReadableArray args) { - switch (commandName) { - case "setNativeDate": - viewManager.setNativeDate(view, (float) args.getDouble(0)); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerInterface.java deleted file mode 100644 index 9df911c7284d3..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/DatePickerManagerInterface.java +++ /dev/null @@ -1,25 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface DatePickerManagerInterface { - void setDate(T view, float value); - void setInitialDate(T view, float value); - void setLocale(T view, @Nullable String value); - void setMaximumDate(T view, float value); - void setMinimumDate(T view, float value); - void setMinuteInterval(T view, @Nullable Integer value); - void setMode(T view, @Nullable String value); - void setTimeZoneOffsetInMinutes(T view, float value); - void setNativeDate(T view, float date); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerDelegate.java deleted file mode 100644 index 52c14f1f607f9..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerDelegate.java +++ /dev/null @@ -1,33 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class InputAccessoryViewManagerDelegate & InputAccessoryViewManagerInterface> extends BaseViewManagerDelegate { - public InputAccessoryViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "backgroundColor": - mViewManager.setBackgroundColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerInterface.java deleted file mode 100644 index 2fa4a4d14a33e..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/InputAccessoryViewManagerInterface.java +++ /dev/null @@ -1,17 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface InputAccessoryViewManagerInterface { - void setBackgroundColor(T view, @Nullable Integer value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerDelegate.java deleted file mode 100644 index 315e5cd181aeb..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerDelegate.java +++ /dev/null @@ -1,26 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class MaskedViewManagerDelegate & MaskedViewManagerInterface> extends BaseViewManagerDelegate { - public MaskedViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - super.setProperty(view, propName, value); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerInterface.java deleted file mode 100644 index 0b930ee79c1be..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/MaskedViewManagerInterface.java +++ /dev/null @@ -1,16 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; - -public interface MaskedViewManagerInterface { - // No props -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerDelegate.java deleted file mode 100644 index 978224f30f73d..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerDelegate.java +++ /dev/null @@ -1,54 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class ModalHostViewManagerDelegate & ModalHostViewManagerInterface> extends BaseViewManagerDelegate { - public ModalHostViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "animationType": - mViewManager.setAnimationType(view, (String) value); - break; - case "presentationStyle": - mViewManager.setPresentationStyle(view, (String) value); - break; - case "transparent": - mViewManager.setTransparent(view, value == null ? false : (boolean) value); - break; - case "statusBarTranslucent": - mViewManager.setStatusBarTranslucent(view, value == null ? false : (boolean) value); - break; - case "hardwareAccelerated": - mViewManager.setHardwareAccelerated(view, value == null ? false : (boolean) value); - break; - case "animated": - mViewManager.setAnimated(view, value == null ? false : (boolean) value); - break; - case "supportedOrientations": - mViewManager.setSupportedOrientations(view, (ReadableArray) value); - break; - case "identifier": - mViewManager.setIdentifier(view, value == null ? 0 : ((Double) value).intValue()); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerInterface.java deleted file mode 100644 index bf60387d5180c..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ModalHostViewManagerInterface.java +++ /dev/null @@ -1,25 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; - -public interface ModalHostViewManagerInterface { - void setAnimationType(T view, @Nullable String value); - void setPresentationStyle(T view, @Nullable String value); - void setTransparent(T view, boolean value); - void setStatusBarTranslucent(T view, boolean value); - void setHardwareAccelerated(T view, boolean value); - void setAnimated(T view, boolean value); - void setSupportedOrientations(T view, @Nullable ReadableArray value); - void setIdentifier(T view, int value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerDelegate.java deleted file mode 100644 index df088d4a87f8b..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerDelegate.java +++ /dev/null @@ -1,49 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class ProgressViewManagerDelegate & ProgressViewManagerInterface> extends BaseViewManagerDelegate { - public ProgressViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "progressViewStyle": - mViewManager.setProgressViewStyle(view, (String) value); - break; - case "progress": - mViewManager.setProgress(view, value == null ? 0f : ((Double) value).floatValue()); - break; - case "progressTintColor": - mViewManager.setProgressTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackTintColor": - mViewManager.setTrackTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "progressImage": - mViewManager.setProgressImage(view, (ReadableMap) value); - break; - case "trackImage": - mViewManager.setTrackImage(view, (ReadableMap) value); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerInterface.java deleted file mode 100644 index bbfbdf4e31bb0..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/ProgressViewManagerInterface.java +++ /dev/null @@ -1,23 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableMap; - -public interface ProgressViewManagerInterface { - void setProgressViewStyle(T view, @Nullable String value); - void setProgress(T view, float value); - void setProgressTintColor(T view, @Nullable Integer value); - void setTrackTintColor(T view, @Nullable Integer value); - void setProgressImage(T view, @Nullable ReadableMap value); - void setTrackImage(T view, @Nullable ReadableMap value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerDelegate.java deleted file mode 100644 index 2d6732ee8d38a..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerDelegate.java +++ /dev/null @@ -1,51 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class PullToRefreshViewManagerDelegate & PullToRefreshViewManagerInterface> extends BaseViewManagerDelegate { - public PullToRefreshViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "tintColor": - mViewManager.setTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "titleColor": - mViewManager.setTitleColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "title": - mViewManager.setTitle(view, value == null ? null : (String) value); - break; - case "refreshing": - mViewManager.setRefreshing(view, value == null ? false : (boolean) value); - break; - default: - super.setProperty(view, propName, value); - } - } - - public void receiveCommand(PullToRefreshViewManagerInterface viewManager, T view, String commandName, ReadableArray args) { - switch (commandName) { - case "setNativeRefreshing": - viewManager.setNativeRefreshing(view, args.getBoolean(0)); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerInterface.java deleted file mode 100644 index 4f40ee89cbba0..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/PullToRefreshViewManagerInterface.java +++ /dev/null @@ -1,21 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface PullToRefreshViewManagerInterface { - void setTintColor(T view, @Nullable Integer value); - void setTitleColor(T view, @Nullable Integer value); - void setTitle(T view, @Nullable String value); - void setRefreshing(T view, boolean value); - void setNativeRefreshing(T view, boolean refreshing); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerDelegate.java deleted file mode 100644 index fdf56b283822c..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerDelegate.java +++ /dev/null @@ -1,32 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class SafeAreaViewManagerDelegate & SafeAreaViewManagerInterface> extends BaseViewManagerDelegate { - public SafeAreaViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "emulateUnlessSupported": - mViewManager.setEmulateUnlessSupported(view, value == null ? false : (boolean) value); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerInterface.java deleted file mode 100644 index 073e2c01c7682..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SafeAreaViewManagerInterface.java +++ /dev/null @@ -1,16 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; - -public interface SafeAreaViewManagerInterface { - void setEmulateUnlessSupported(T view, boolean value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerDelegate.java deleted file mode 100644 index 6763a1f1f53f0..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerDelegate.java +++ /dev/null @@ -1,52 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class SegmentedControlManagerDelegate & SegmentedControlManagerInterface> extends BaseViewManagerDelegate { - public SegmentedControlManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "values": - mViewManager.setValues(view, (ReadableArray) value); - break; - case "selectedIndex": - mViewManager.setSelectedIndex(view, value == null ? 0 : ((Double) value).intValue()); - break; - case "enabled": - mViewManager.setEnabled(view, value == null ? true : (boolean) value); - break; - case "tintColor": - mViewManager.setTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "textColor": - mViewManager.setTextColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "backgroundColor": - mViewManager.setBackgroundColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "momentary": - mViewManager.setMomentary(view, value == null ? false : (boolean) value); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerInterface.java deleted file mode 100644 index 0ee9d56850941..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SegmentedControlManagerInterface.java +++ /dev/null @@ -1,24 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; - -public interface SegmentedControlManagerInterface { - void setValues(T view, @Nullable ReadableArray value); - void setSelectedIndex(T view, int value); - void setEnabled(T view, boolean value); - void setTintColor(T view, @Nullable Integer value); - void setTextColor(T view, @Nullable Integer value); - void setBackgroundColor(T view, @Nullable Integer value); - void setMomentary(T view, boolean value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerDelegate.java deleted file mode 100644 index 5eb9360ef8e28..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerDelegate.java +++ /dev/null @@ -1,73 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class SliderManagerDelegate & SliderManagerInterface> extends BaseViewManagerDelegate { - public SliderManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "disabled": - mViewManager.setDisabled(view, value == null ? false : (boolean) value); - break; - case "enabled": - mViewManager.setEnabled(view, value == null ? true : (boolean) value); - break; - case "maximumTrackImage": - mViewManager.setMaximumTrackImage(view, (ReadableMap) value); - break; - case "maximumTrackTintColor": - mViewManager.setMaximumTrackTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "maximumValue": - mViewManager.setMaximumValue(view, value == null ? 1f : ((Double) value).doubleValue()); - break; - case "minimumTrackImage": - mViewManager.setMinimumTrackImage(view, (ReadableMap) value); - break; - case "minimumTrackTintColor": - mViewManager.setMinimumTrackTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "minimumValue": - mViewManager.setMinimumValue(view, value == null ? 0f : ((Double) value).doubleValue()); - break; - case "step": - mViewManager.setStep(view, value == null ? 0f : ((Double) value).doubleValue()); - break; - case "testID": - mViewManager.setTestID(view, value == null ? "" : (String) value); - break; - case "thumbImage": - mViewManager.setThumbImage(view, (ReadableMap) value); - break; - case "thumbTintColor": - mViewManager.setThumbTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackImage": - mViewManager.setTrackImage(view, (ReadableMap) value); - break; - case "value": - mViewManager.setValue(view, value == null ? 0f : ((Double) value).doubleValue()); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerInterface.java deleted file mode 100644 index 2d5c752f62cbb..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SliderManagerInterface.java +++ /dev/null @@ -1,31 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableMap; - -public interface SliderManagerInterface { - void setDisabled(T view, boolean value); - void setEnabled(T view, boolean value); - void setMaximumTrackImage(T view, @Nullable ReadableMap value); - void setMaximumTrackTintColor(T view, @Nullable Integer value); - void setMaximumValue(T view, double value); - void setMinimumTrackImage(T view, @Nullable ReadableMap value); - void setMinimumTrackTintColor(T view, @Nullable Integer value); - void setMinimumValue(T view, double value); - void setStep(T view, double value); - void setTestID(T view, @Nullable String value); - void setThumbImage(T view, @Nullable ReadableMap value); - void setThumbTintColor(T view, @Nullable Integer value); - void setTrackImage(T view, @Nullable ReadableMap value); - void setValue(T view, double value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java deleted file mode 100644 index 65d557ced192e..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java +++ /dev/null @@ -1,63 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; - -public class SwitchManagerDelegate & SwitchManagerInterface> extends BaseViewManagerDelegate { - public SwitchManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "disabled": - mViewManager.setDisabled(view, value == null ? false : (boolean) value); - break; - case "value": - mViewManager.setValue(view, value == null ? false : (boolean) value); - break; - case "tintColor": - mViewManager.setTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "onTintColor": - mViewManager.setOnTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "thumbTintColor": - mViewManager.setThumbTintColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "thumbColor": - mViewManager.setThumbColor(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackColorForFalse": - mViewManager.setTrackColorForFalse(view, ColorPropConverter.getColor(value, view.getContext())); - break; - case "trackColorForTrue": - mViewManager.setTrackColorForTrue(view, ColorPropConverter.getColor(value, view.getContext())); - break; - default: - super.setProperty(view, propName, value); - } - } - - @Override - public void receiveCommand(T view, String commandName, ReadableArray args) { - switch (commandName) { - case "setValue": - mViewManager.setValue(view, args.getBoolean(0)); - break; - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerInterface.java deleted file mode 100644 index 04dce5cd1f3dc..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerInterface.java +++ /dev/null @@ -1,24 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface SwitchManagerInterface { - void setDisabled(T view, boolean value); - void setValue(T view, boolean value); - void setTintColor(T view, @Nullable Integer value); - void setOnTintColor(T view, @Nullable Integer value); - void setThumbTintColor(T view, @Nullable Integer value); - void setThumbColor(T view, @Nullable Integer value); - void setTrackColorForFalse(T view, @Nullable Integer value); - void setTrackColorForTrue(T view, @Nullable Integer value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerDelegate.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerDelegate.java deleted file mode 100644 index f90b2c3b25fa1..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerDelegate.java +++ /dev/null @@ -1,32 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaDelegate.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; -import com.facebook.react.uimanager.LayoutShadowNode; - -public class UnimplementedNativeViewManagerDelegate & UnimplementedNativeViewManagerInterface> extends BaseViewManagerDelegate { - public UnimplementedNativeViewManagerDelegate(U viewManager) { - super(viewManager); - } - @Override - public void setProperty(T view, String propName, @Nullable Object value) { - switch (propName) { - case "name": - mViewManager.setName(view, value == null ? "" : (String) value); - break; - default: - super.setProperty(view, propName, value); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerInterface.java b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerInterface.java deleted file mode 100644 index c76951e2225c1..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/UnimplementedNativeViewManagerInterface.java +++ /dev/null @@ -1,17 +0,0 @@ -/** -* Copyright (c) Facebook, Inc. and its affiliates. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -* -* @generated by codegen project: GeneratePropsJavaInterface.js -*/ - -package com.facebook.react.viewmanagers; - -import android.view.View; -import androidx.annotation.Nullable; - -public interface UnimplementedNativeViewManagerInterface { - void setName(T view, @Nullable String value); -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/Android.mk b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/Android.mk deleted file mode 100644 index cc37b42fdbe2e..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := react_render_viewmanagers - -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp $(LOCAL_PATH)/react/renderer/components/rncore/*.cpp) - -LOCAL_SHARED_LIBRARIES := libreact_render_components_view libfolly_json libreact_render_core libreact_render_graphics - -LOCAL_STATIC_LIBRARIES := - -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/react/renderer/components/rncore/ - -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) - -LOCAL_CFLAGS := \ - -DLOG_TAG=\"Fabric\" - -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall - -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,react/renderer/components/view) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.cpp b/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.cpp deleted file mode 100644 index fcce261612f83..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.cpp +++ /dev/null @@ -1,191 +0,0 @@ - -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @generated by codegen project: GeneratePropsCpp.js - */ - -#include -#include -#include - -namespace facebook { -namespace react { - -ActivityIndicatorViewProps::ActivityIndicatorViewProps( - const ActivityIndicatorViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - hidesWhenStopped(convertRawProp(rawProps, "hidesWhenStopped", sourceProps.hidesWhenStopped, {false})), - animating(convertRawProp(rawProps, "animating", sourceProps.animating, {false})), - color(convertRawProp(rawProps, "color", sourceProps.color, {})), - size(convertRawProp(rawProps, "size", sourceProps.size, {ActivityIndicatorViewSize::Small})) - {} -DatePickerProps::DatePickerProps( - const DatePickerProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - date(convertRawProp(rawProps, "date", sourceProps.date, {0.0})), - initialDate(convertRawProp(rawProps, "initialDate", sourceProps.initialDate, {0.0})), - locale(convertRawProp(rawProps, "locale", sourceProps.locale, {})), - maximumDate(convertRawProp(rawProps, "maximumDate", sourceProps.maximumDate, {0.0})), - minimumDate(convertRawProp(rawProps, "minimumDate", sourceProps.minimumDate, {0.0})), - minuteInterval(convertRawProp(rawProps, "minuteInterval", sourceProps.minuteInterval, {DatePickerMinuteInterval::MinuteInterval1})), - mode(convertRawProp(rawProps, "mode", sourceProps.mode, {DatePickerMode::Date})), - timeZoneOffsetInMinutes(convertRawProp(rawProps, "timeZoneOffsetInMinutes", sourceProps.timeZoneOffsetInMinutes, {0.0})) - {} -AndroidDrawerLayoutProps::AndroidDrawerLayoutProps( - const AndroidDrawerLayoutProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - keyboardDismissMode(convertRawProp(rawProps, "keyboardDismissMode", sourceProps.keyboardDismissMode, {AndroidDrawerLayoutKeyboardDismissMode::None})), - drawerBackgroundColor(convertRawProp(rawProps, "drawerBackgroundColor", sourceProps.drawerBackgroundColor, {})), - drawerPosition(convertRawProp(rawProps, "drawerPosition", sourceProps.drawerPosition, {AndroidDrawerLayoutDrawerPosition::Left})), - drawerWidth(convertRawProp(rawProps, "drawerWidth", sourceProps.drawerWidth, {})), - drawerLockMode(convertRawProp(rawProps, "drawerLockMode", sourceProps.drawerLockMode, {AndroidDrawerLayoutDrawerLockMode::Unlocked})), - statusBarBackgroundColor(convertRawProp(rawProps, "statusBarBackgroundColor", sourceProps.statusBarBackgroundColor, {})) - {} -RCTMaskedViewProps::RCTMaskedViewProps( - const RCTMaskedViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps) - - - {} -AndroidProgressBarProps::AndroidProgressBarProps( - const AndroidProgressBarProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - styleAttr(convertRawProp(rawProps, "styleAttr", sourceProps.styleAttr, {})), - typeAttr(convertRawProp(rawProps, "typeAttr", sourceProps.typeAttr, {})), - indeterminate(convertRawProp(rawProps, "indeterminate", sourceProps.indeterminate, {false})), - progress(convertRawProp(rawProps, "progress", sourceProps.progress, {0.0})), - animating(convertRawProp(rawProps, "animating", sourceProps.animating, {true})), - color(convertRawProp(rawProps, "color", sourceProps.color, {})), - testID(convertRawProp(rawProps, "testID", sourceProps.testID, {""})) - {} -RCTProgressViewProps::RCTProgressViewProps( - const RCTProgressViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - progressViewStyle(convertRawProp(rawProps, "progressViewStyle", sourceProps.progressViewStyle, {RCTProgressViewProgressViewStyle::Default})), - progress(convertRawProp(rawProps, "progress", sourceProps.progress, {0.0})), - progressTintColor(convertRawProp(rawProps, "progressTintColor", sourceProps.progressTintColor, {})), - trackTintColor(convertRawProp(rawProps, "trackTintColor", sourceProps.trackTintColor, {})), - progressImage(convertRawProp(rawProps, "progressImage", sourceProps.progressImage, {})), - trackImage(convertRawProp(rawProps, "trackImage", sourceProps.trackImage, {})) - {} -AndroidSwipeRefreshLayoutProps::AndroidSwipeRefreshLayoutProps( - const AndroidSwipeRefreshLayoutProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - enabled(convertRawProp(rawProps, "enabled", sourceProps.enabled, {true})), - colors(convertRawProp(rawProps, "colors", sourceProps.colors, {})), - progressBackgroundColor(convertRawProp(rawProps, "progressBackgroundColor", sourceProps.progressBackgroundColor, {})), - size(convertRawProp(rawProps, "size", sourceProps.size, {1})), - progressViewOffset(convertRawProp(rawProps, "progressViewOffset", sourceProps.progressViewOffset, {0.0})), - refreshing(convertRawProp(rawProps, "refreshing", sourceProps.refreshing, {false})) - {} -PullToRefreshViewProps::PullToRefreshViewProps( - const PullToRefreshViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - tintColor(convertRawProp(rawProps, "tintColor", sourceProps.tintColor, {})), - titleColor(convertRawProp(rawProps, "titleColor", sourceProps.titleColor, {})), - title(convertRawProp(rawProps, "title", sourceProps.title, {})), - refreshing(convertRawProp(rawProps, "refreshing", sourceProps.refreshing, {false})) - {} -SafeAreaViewProps::SafeAreaViewProps( - const SafeAreaViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - emulateUnlessSupported(convertRawProp(rawProps, "emulateUnlessSupported", sourceProps.emulateUnlessSupported, {false})) - {} -RCTSegmentedControlProps::RCTSegmentedControlProps( - const RCTSegmentedControlProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - values(convertRawProp(rawProps, "values", sourceProps.values, {})), - selectedIndex(convertRawProp(rawProps, "selectedIndex", sourceProps.selectedIndex, {0})), - enabled(convertRawProp(rawProps, "enabled", sourceProps.enabled, {true})), - tintColor(convertRawProp(rawProps, "tintColor", sourceProps.tintColor, {})), - textColor(convertRawProp(rawProps, "textColor", sourceProps.textColor, {})), - backgroundColor(convertRawProp(rawProps, "backgroundColor", sourceProps.backgroundColor, {})), - momentary(convertRawProp(rawProps, "momentary", sourceProps.momentary, {false})) - {} -SliderProps::SliderProps( - const SliderProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - disabled(convertRawProp(rawProps, "disabled", sourceProps.disabled, {false})), - enabled(convertRawProp(rawProps, "enabled", sourceProps.enabled, {true})), - maximumTrackImage(convertRawProp(rawProps, "maximumTrackImage", sourceProps.maximumTrackImage, {})), - maximumTrackTintColor(convertRawProp(rawProps, "maximumTrackTintColor", sourceProps.maximumTrackTintColor, {})), - maximumValue(convertRawProp(rawProps, "maximumValue", sourceProps.maximumValue, {1.0})), - minimumTrackImage(convertRawProp(rawProps, "minimumTrackImage", sourceProps.minimumTrackImage, {})), - minimumTrackTintColor(convertRawProp(rawProps, "minimumTrackTintColor", sourceProps.minimumTrackTintColor, {})), - minimumValue(convertRawProp(rawProps, "minimumValue", sourceProps.minimumValue, {0.0})), - step(convertRawProp(rawProps, "step", sourceProps.step, {0.0})), - testID(convertRawProp(rawProps, "testID", sourceProps.testID, {""})), - thumbImage(convertRawProp(rawProps, "thumbImage", sourceProps.thumbImage, {})), - thumbTintColor(convertRawProp(rawProps, "thumbTintColor", sourceProps.thumbTintColor, {})), - trackImage(convertRawProp(rawProps, "trackImage", sourceProps.trackImage, {})), - value(convertRawProp(rawProps, "value", sourceProps.value, {0.0})) - {} -AndroidSwitchProps::AndroidSwitchProps( - const AndroidSwitchProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - disabled(convertRawProp(rawProps, "disabled", sourceProps.disabled, {false})), - enabled(convertRawProp(rawProps, "enabled", sourceProps.enabled, {true})), - thumbColor(convertRawProp(rawProps, "thumbColor", sourceProps.thumbColor, {})), - trackColorForFalse(convertRawProp(rawProps, "trackColorForFalse", sourceProps.trackColorForFalse, {})), - trackColorForTrue(convertRawProp(rawProps, "trackColorForTrue", sourceProps.trackColorForTrue, {})), - value(convertRawProp(rawProps, "value", sourceProps.value, {false})), - on(convertRawProp(rawProps, "on", sourceProps.on, {false})), - thumbTintColor(convertRawProp(rawProps, "thumbTintColor", sourceProps.thumbTintColor, {})), - trackTintColor(convertRawProp(rawProps, "trackTintColor", sourceProps.trackTintColor, {})) - {} -SwitchProps::SwitchProps( - const SwitchProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - disabled(convertRawProp(rawProps, "disabled", sourceProps.disabled, {false})), - value(convertRawProp(rawProps, "value", sourceProps.value, {false})), - tintColor(convertRawProp(rawProps, "tintColor", sourceProps.tintColor, {})), - onTintColor(convertRawProp(rawProps, "onTintColor", sourceProps.onTintColor, {})), - thumbTintColor(convertRawProp(rawProps, "thumbTintColor", sourceProps.thumbTintColor, {})), - thumbColor(convertRawProp(rawProps, "thumbColor", sourceProps.thumbColor, {})), - trackColorForFalse(convertRawProp(rawProps, "trackColorForFalse", sourceProps.trackColorForFalse, {})), - trackColorForTrue(convertRawProp(rawProps, "trackColorForTrue", sourceProps.trackColorForTrue, {})) - {} -InputAccessoryProps::InputAccessoryProps( - const InputAccessoryProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - backgroundColor(convertRawProp(rawProps, "backgroundColor", sourceProps.backgroundColor, {})) - {} -UnimplementedNativeViewProps::UnimplementedNativeViewProps( - const UnimplementedNativeViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - name(convertRawProp(rawProps, "name", sourceProps.name, {""})) - {} -ModalHostViewProps::ModalHostViewProps( - const ModalHostViewProps &sourceProps, - const RawProps &rawProps): ViewProps(sourceProps, rawProps), - - animationType(convertRawProp(rawProps, "animationType", sourceProps.animationType, {ModalHostViewAnimationType::None})), - presentationStyle(convertRawProp(rawProps, "presentationStyle", sourceProps.presentationStyle, {ModalHostViewPresentationStyle::FullScreen})), - transparent(convertRawProp(rawProps, "transparent", sourceProps.transparent, {false})), - statusBarTranslucent(convertRawProp(rawProps, "statusBarTranslucent", sourceProps.statusBarTranslucent, {false})), - hardwareAccelerated(convertRawProp(rawProps, "hardwareAccelerated", sourceProps.hardwareAccelerated, {false})), - animated(convertRawProp(rawProps, "animated", sourceProps.animated, {false})), - supportedOrientations(convertRawProp(rawProps, "supportedOrientations", sourceProps.supportedOrientations, {static_cast(ModalHostViewSupportedOrientations::Portrait)})), - identifier(convertRawProp(rawProps, "identifier", sourceProps.identifier, {0})) - {} - -} // namespace react -} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK index 2b6f2c1e932a8..720c0618c0f9a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "common", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK index ac71cc153ec77..a921e7eedcb5b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "drawer", srcs = glob(["**/*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -25,6 +26,6 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/views/scroll:scroll"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), + react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java index 712353eb0a42f..b3d9cc11bcdc2 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java @@ -20,7 +20,7 @@ import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.ViewManagerDelegate; import com.facebook.react.uimanager.annotations.ReactProp; @@ -56,12 +56,13 @@ public ReactDrawerLayoutManager() { @Override protected void addEventEmitters(ThemedReactContext reactContext, ReactDrawerLayout view) { - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); - if (uiManager == null) { + EventDispatcher eventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.getId()); + if (eventDispatcher == null) { return; } - view.addDrawerListener(new DrawerEventEmitter(view, uiManager.getEventDispatcher())); + view.addDrawerListener(new DrawerEventEmitter(view, eventDispatcher)); } @Override @@ -252,22 +253,30 @@ public DrawerEventEmitter(DrawerLayout drawerLayout, EventDispatcher eventDispat @Override public void onDrawerSlide(@NonNull View view, float v) { - mEventDispatcher.dispatchEvent(new DrawerSlideEvent(mDrawerLayout.getId(), v)); + mEventDispatcher.dispatchEvent( + new DrawerSlideEvent( + UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId(), v)); } @Override public void onDrawerOpened(@NonNull View view) { - mEventDispatcher.dispatchEvent(new DrawerOpenedEvent(mDrawerLayout.getId())); + mEventDispatcher.dispatchEvent( + new DrawerOpenedEvent( + UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId())); } @Override public void onDrawerClosed(@NonNull View view) { - mEventDispatcher.dispatchEvent(new DrawerClosedEvent(mDrawerLayout.getId())); + mEventDispatcher.dispatchEvent( + new DrawerClosedEvent( + UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId())); } @Override public void onDrawerStateChanged(int i) { - mEventDispatcher.dispatchEvent(new DrawerStateChangedEvent(mDrawerLayout.getId(), i)); + mEventDispatcher.dispatchEvent( + new DrawerStateChangedEvent( + UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId(), i)); } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerClosedEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerClosedEvent.java index e6bd567032fa4..29c724c1ada91 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerClosedEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerClosedEvent.java @@ -8,30 +8,29 @@ package com.facebook.react.views.drawer.events; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; public class DrawerClosedEvent extends Event { public static final String EVENT_NAME = "topDrawerClose"; + @Deprecated public DrawerClosedEvent(int viewId) { - super(viewId); + this(-1, viewId); } - @Override - public String getEventName() { - return EVENT_NAME; + public DrawerClosedEvent(int surfaceId, int viewId) { + super(surfaceId, viewId); } @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; + public String getEventName() { + return EVENT_NAME; } @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), Arguments.createMap()); + protected WritableMap getEventData() { + return Arguments.createMap(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerOpenedEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerOpenedEvent.java index 2e754dc520799..d1402f593cece 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerOpenedEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerOpenedEvent.java @@ -8,30 +8,29 @@ package com.facebook.react.views.drawer.events; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; public class DrawerOpenedEvent extends Event { public static final String EVENT_NAME = "topDrawerOpen"; + @Deprecated public DrawerOpenedEvent(int viewId) { - super(viewId); + this(-1, viewId); } - @Override - public String getEventName() { - return EVENT_NAME; + public DrawerOpenedEvent(int surfaceId, int viewId) { + super(surfaceId, viewId); } @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; + public String getEventName() { + return EVENT_NAME; } @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), Arguments.createMap()); + protected WritableMap getEventData() { + return Arguments.createMap(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerSlideEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerSlideEvent.java index 7d3a0af18dded..57f2ef5fa60ac 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerSlideEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerSlideEvent.java @@ -10,7 +10,6 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by a DrawerLayout as it is being moved open/closed. */ public class DrawerSlideEvent extends Event { @@ -19,8 +18,13 @@ public class DrawerSlideEvent extends Event { private final float mOffset; + @Deprecated public DrawerSlideEvent(int viewId, float offset) { - super(viewId); + this(-1, viewId, offset); + } + + public DrawerSlideEvent(int surfaceId, int viewId, float offset) { + super(surfaceId, viewId); mOffset = offset; } @@ -34,17 +38,7 @@ public String getEventName() { } @Override - public short getCoalescingKey() { - // All slide events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putDouble("offset", getOffset()); return eventData; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerStateChangedEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerStateChangedEvent.java index 009217934d3d6..6a9f8e9fb06d0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerStateChangedEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/drawer/events/DrawerStateChangedEvent.java @@ -10,7 +10,6 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; public class DrawerStateChangedEvent extends Event { @@ -18,8 +17,13 @@ public class DrawerStateChangedEvent extends Event { private final int mDrawerState; + @Deprecated public DrawerStateChangedEvent(int viewId, int drawerState) { - super(viewId); + this(-1, viewId, drawerState); + } + + public DrawerStateChangedEvent(int surfaceId, int viewId, int drawerState) { + super(surfaceId, viewId); mDrawerState = drawerState; } @@ -33,17 +37,7 @@ public String getEventName() { } @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putDouble("drawerState", getDrawerState()); return eventData; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK index 663357ac133d7..40f0d2f5592b3 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK @@ -7,6 +7,7 @@ IMAGE_EVENT_FILES = [ rn_android_library( name = "imageevents", srcs = IMAGE_EVENT_FILES, + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -31,6 +32,7 @@ rn_android_library( ["*.java"], exclude = IMAGE_EVENT_FILES, ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageLoadEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageLoadEvent.java index 69778f028f5a9..130cb67ad8b60 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageLoadEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageLoadEvent.java @@ -12,7 +12,6 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,8 +34,35 @@ public class ImageLoadEvent extends Event { private final int mLoaded; private final int mTotal; + @Deprecated public static final ImageLoadEvent createLoadStartEvent(int viewId) { - return new ImageLoadEvent(viewId, ON_LOAD_START); + return createLoadStartEvent(-1, viewId); + } + + @Deprecated + public static final ImageLoadEvent createProgressEvent( + int viewId, @Nullable String imageUri, int loaded, int total) { + return createProgressEvent(-1, viewId, imageUri, loaded, total); + } + + @Deprecated + public static final ImageLoadEvent createLoadEvent( + int viewId, @Nullable String imageUri, int width, int height) { + return createLoadEvent(-1, viewId, imageUri, width, height); + } + + @Deprecated + public static final ImageLoadEvent createErrorEvent(int viewId, Throwable throwable) { + return createErrorEvent(-1, viewId, throwable); + } + + @Deprecated + public static final ImageLoadEvent createLoadEndEvent(int viewId) { + return createLoadEndEvent(-1, viewId); + } + + public static final ImageLoadEvent createLoadStartEvent(int surfaceId, int viewId) { + return new ImageLoadEvent(surfaceId, viewId, ON_LOAD_START); } /** @@ -45,28 +71,31 @@ public static final ImageLoadEvent createLoadStartEvent(int viewId) { * @param total Amount that `loaded` will be when the image is fully loaded. */ public static final ImageLoadEvent createProgressEvent( - int viewId, @Nullable String imageUri, int loaded, int total) { - return new ImageLoadEvent(viewId, ON_PROGRESS, null, imageUri, 0, 0, loaded, total); + int surfaceId, int viewId, @Nullable String imageUri, int loaded, int total) { + return new ImageLoadEvent(surfaceId, viewId, ON_PROGRESS, null, imageUri, 0, 0, loaded, total); } public static final ImageLoadEvent createLoadEvent( - int viewId, @Nullable String imageUri, int width, int height) { - return new ImageLoadEvent(viewId, ON_LOAD, null, imageUri, width, height, 0, 0); + int surfaceId, int viewId, @Nullable String imageUri, int width, int height) { + return new ImageLoadEvent(surfaceId, viewId, ON_LOAD, null, imageUri, width, height, 0, 0); } - public static final ImageLoadEvent createErrorEvent(int viewId, Throwable throwable) { - return new ImageLoadEvent(viewId, ON_ERROR, throwable.getMessage(), null, 0, 0, 0, 0); + public static final ImageLoadEvent createErrorEvent( + int surfaceId, int viewId, Throwable throwable) { + return new ImageLoadEvent( + surfaceId, viewId, ON_ERROR, throwable.getMessage(), null, 0, 0, 0, 0); } - public static final ImageLoadEvent createLoadEndEvent(int viewId) { - return new ImageLoadEvent(viewId, ON_LOAD_END); + public static final ImageLoadEvent createLoadEndEvent(int surfaceId, int viewId) { + return new ImageLoadEvent(surfaceId, viewId, ON_LOAD_END); } - private ImageLoadEvent(int viewId, @ImageEventType int eventType) { - this(viewId, eventType, null, null, 0, 0, 0, 0); + private ImageLoadEvent(int surfaceId, int viewId, @ImageEventType int eventType) { + this(surfaceId, viewId, eventType, null, null, 0, 0, 0, 0); } private ImageLoadEvent( + int surfaceId, int viewId, @ImageEventType int eventType, @Nullable String errorMessage, @@ -75,7 +104,7 @@ private ImageLoadEvent( int height, int loaded, int total) { - super(viewId); + super(surfaceId, viewId); mEventType = eventType; mErrorMessage = errorMessage; mSourceUri = sourceUri; @@ -115,26 +144,23 @@ public short getCoalescingKey() { } @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - WritableMap eventData = null; + protected WritableMap getEventData() { + WritableMap eventData = Arguments.createMap(); switch (mEventType) { case ON_PROGRESS: - eventData = Arguments.createMap(); eventData.putInt("loaded", mLoaded); eventData.putInt("total", mTotal); break; case ON_LOAD: - eventData = Arguments.createMap(); eventData.putMap("source", createEventDataSource()); break; case ON_ERROR: - eventData = Arguments.createMap(); eventData.putString("error", mErrorMessage); break; } - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData); + return eventData; } private WritableMap createEventDataSource() { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactCallerContextFactory.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactCallerContextFactory.java index 46e9573fdb0ca..cb1c6a336048e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactCallerContextFactory.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactCallerContextFactory.java @@ -20,9 +20,9 @@ public interface ReactCallerContextFactory { /** * This method will be called at the time {@link ReactImageManager} creates {@link ReactImageView} * - * @param surfaceID {@link String} used to log the name of the surface + * @param surfaceName {@link String} used to log the name of the surface * @return an {@link Object} that represents the CallerContext. */ @Nullable - Object getOrCreateCallerContext(@Nullable String surfaceID, @Nullable String analyticTag); + Object getOrCreateCallerContext(@Nullable String surfaceName, @Nullable String analyticTag); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java index 397a8378d603f..646cc5fc59414 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java @@ -31,11 +31,6 @@ public class ReactImageManager extends SimpleViewManager { public static final String REACT_CLASS = "RCTImageView"; - @Override - public String getName() { - return REACT_CLASS; - } - private @Nullable AbstractDraweeControllerBuilder mDraweeControllerBuilder; private @Nullable GlobalImageLoadListener mGlobalImageLoadListener; private final @Nullable Object mCallerContext; @@ -107,12 +102,22 @@ public Object getCallerContext() { public ReactImageView createViewInstance(ThemedReactContext context) { Object callerContext = mCallerContextFactory != null - ? mCallerContextFactory.getOrCreateCallerContext(context.getSurfaceID(), null) + ? mCallerContextFactory.getOrCreateCallerContext(context.getModuleName(), null) : getCallerContext(); return new ReactImageView( context, getDraweeControllerBuilder(), mGlobalImageLoadListener, callerContext); } + @Override + public String getName() { + return REACT_CLASS; + } + + @ReactProp(name = "accessible") + public void setAccessible(ReactImageView view, boolean accessible) { + view.setFocusable(accessible); + } + // In JS this is Image.props.source @ReactProp(name = "src") public void setSource(ReactImageView view, @Nullable ReadableArray sources) { @@ -129,7 +134,7 @@ public void setInternal_AnalyticsTag(ReactImageView view, @Nullable String analy if (mCallerContextFactory != null) { view.updateCallerContext( mCallerContextFactory.getOrCreateCallerContext( - ((ThemedReactContext) view.getContext()).getSurfaceID(), analyticTag)); + ((ThemedReactContext) view.getContext()).getModuleName(), analyticTag)); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java index 68ada3dfc7062..a25658a27f2bb 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.widget.Toast; import androidx.annotation.Nullable; +import com.facebook.common.internal.Objects; import com.facebook.common.references.CloseableReference; import com.facebook.common.util.UriUtil; import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; @@ -90,8 +91,10 @@ public class ReactImageView extends GenericDraweeView { private ImageResizeMethod mResizeMethod = ImageResizeMethod.AUTO; public void updateCallerContext(@Nullable Object callerContext) { - mCallerContext = callerContext; - mIsDirty = true; + if (!Objects.equal(mCallerContext, callerContext)) { + mCallerContext = callerContext; + mIsDirty = true; + } } private class RoundedCornerPostprocessor extends BasePostprocessor { @@ -229,6 +232,11 @@ public ReactImageView( } public void setShouldNotifyLoadEvents(boolean shouldNotify) { + // Skip update if shouldNotify is already in sync with the download listener + if (shouldNotify == (mDownloadListener != null)) { + return; + } + if (!shouldNotify) { mDownloadListener = null; } else { @@ -242,12 +250,18 @@ public void onProgressChange(int loaded, int total) { // TODO: Somehow get image size and convert `loaded` and `total` to image bytes. mEventDispatcher.dispatchEvent( ImageLoadEvent.createProgressEvent( - getId(), mImageSource.getSource(), loaded, total)); + UIManagerHelper.getSurfaceId(ReactImageView.this), + getId(), + mImageSource.getSource(), + loaded, + total)); } @Override public void onSubmit(String id, Object callerContext) { - mEventDispatcher.dispatchEvent(ImageLoadEvent.createLoadStartEvent(getId())); + mEventDispatcher.dispatchEvent( + ImageLoadEvent.createLoadStartEvent( + UIManagerHelper.getSurfaceId(ReactImageView.this), getId())); } @Override @@ -256,17 +270,22 @@ public void onFinalImageSet( if (imageInfo != null) { mEventDispatcher.dispatchEvent( ImageLoadEvent.createLoadEvent( + UIManagerHelper.getSurfaceId(ReactImageView.this), getId(), mImageSource.getSource(), imageInfo.getWidth(), imageInfo.getHeight())); - mEventDispatcher.dispatchEvent(ImageLoadEvent.createLoadEndEvent(getId())); + mEventDispatcher.dispatchEvent( + ImageLoadEvent.createLoadEndEvent( + UIManagerHelper.getSurfaceId(ReactImageView.this), getId())); } } @Override public void onFailure(String id, Throwable throwable) { - mEventDispatcher.dispatchEvent(ImageLoadEvent.createErrorEvent(getId(), throwable)); + mEventDispatcher.dispatchEvent( + ImageLoadEvent.createErrorEvent( + UIManagerHelper.getSurfaceId(ReactImageView.this), getId(), throwable)); } }; } @@ -295,18 +314,25 @@ public void setBackgroundColor(int backgroundColor) { } public void setBorderColor(int borderColor) { - mBorderColor = borderColor; - mIsDirty = true; + if (mBorderColor != borderColor) { + mBorderColor = borderColor; + mIsDirty = true; + } } public void setOverlayColor(int overlayColor) { - mOverlayColor = overlayColor; - mIsDirty = true; + if (mOverlayColor != overlayColor) { + mOverlayColor = overlayColor; + mIsDirty = true; + } } public void setBorderWidth(float borderWidth) { - mBorderWidth = PixelUtil.toPixelFromDIP(borderWidth); - mIsDirty = true; + float newBorderWidth = PixelUtil.toPixelFromDIP(borderWidth); + if (!FloatUtil.floatsEqual(mBorderWidth, newBorderWidth)) { + mBorderWidth = newBorderWidth; + mIsDirty = true; + } } public void setBorderRadius(float borderRadius) { @@ -329,32 +355,39 @@ public void setBorderRadius(float borderRadius, int position) { } public void setScaleType(ScalingUtils.ScaleType scaleType) { - mScaleType = scaleType; - mIsDirty = true; + if (mScaleType != scaleType) { + mScaleType = scaleType; + mIsDirty = true; + } } public void setTileMode(Shader.TileMode tileMode) { - mTileMode = tileMode; - mIsDirty = true; + if (mTileMode != tileMode) { + mTileMode = tileMode; + mIsDirty = true; + } } public void setResizeMethod(ImageResizeMethod resizeMethod) { - mResizeMethod = resizeMethod; - mIsDirty = true; + if (mResizeMethod != resizeMethod) { + mResizeMethod = resizeMethod; + mIsDirty = true; + } } public void setSource(@Nullable ReadableArray sources) { - mSources.clear(); + List tmpSources = new LinkedList<>(); + if (sources == null || sources.size() == 0) { ImageSource imageSource = new ImageSource(getContext(), REMOTE_TRANSPARENT_BITMAP_URI); - mSources.add(imageSource); + tmpSources.add(imageSource); } else { // Optimize for the case where we have just one uri, case in which we don't need the sizes if (sources.size() == 1) { ReadableMap source = sources.getMap(0); String uri = source.getString("uri"); ImageSource imageSource = new ImageSource(getContext(), uri); - mSources.add(imageSource); + tmpSources.add(imageSource); if (Uri.EMPTY.equals(imageSource.getUri())) { warnImageSource(uri); } @@ -365,28 +398,44 @@ public void setSource(@Nullable ReadableArray sources) { ImageSource imageSource = new ImageSource( getContext(), uri, source.getDouble("width"), source.getDouble("height")); - mSources.add(imageSource); + tmpSources.add(imageSource); if (Uri.EMPTY.equals(imageSource.getUri())) { warnImageSource(uri); } } } } + + // Don't reset sources and dirty node if sources haven't changed + if (mSources.equals(tmpSources)) { + return; + } + + mSources.clear(); + for (ImageSource src : tmpSources) { + mSources.add(src); + } mIsDirty = true; } public void setDefaultSource(@Nullable String name) { - mDefaultImageDrawable = + Drawable newDefaultDrawable = ResourceDrawableIdHelper.getInstance().getResourceDrawable(getContext(), name); - mIsDirty = true; + if (!Objects.equal(mDefaultImageDrawable, newDefaultDrawable)) { + mDefaultImageDrawable = newDefaultDrawable; + mIsDirty = true; + } } public void setLoadingIndicatorSource(@Nullable String name) { Drawable drawable = ResourceDrawableIdHelper.getInstance().getResourceDrawable(getContext(), name); - mLoadingImageDrawable = + Drawable newLoadingIndicatorSource = drawable != null ? (Drawable) new AutoRotateDrawable(drawable, 1000) : null; - mIsDirty = true; + if (!Objects.equal(mLoadingImageDrawable, newLoadingIndicatorSource)) { + mLoadingImageDrawable = newLoadingIndicatorSource; + mIsDirty = true; + } } public void setProgressiveRenderingEnabled(boolean enabled) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK index 4e9cafe129a02..3776c3389f849 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK @@ -6,6 +6,7 @@ rn_android_library( ["*.java"], exclude = ["MultiSourceHelper.java"], ), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -21,6 +22,7 @@ rn_android_library( rn_android_library( name = "withmultisource", srcs = ["MultiSourceHelper.java"], + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ImageSource.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ImageSource.java index 9f95b5bae9af0..065ebbe636682 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ImageSource.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/ImageSource.java @@ -11,6 +11,7 @@ import android.net.Uri; import androidx.annotation.Nullable; import com.facebook.infer.annotation.Assertions; +import java.util.Objects; /** Class describing an image source (network URI or resource) and size. */ public class ImageSource { @@ -29,6 +30,22 @@ public ImageSource(Context context, String source, double width, double height) mUri = computeUri(context); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImageSource that = (ImageSource) o; + return Double.compare(that.mSize, mSize) == 0 + && isResource == that.isResource + && Objects.equals(mUri, that.mUri) + && Objects.equals(mSource, that.mSource); + } + + @Override + public int hashCode() { + return Objects.hash(mUri, mSource, mSize, isResource); + } + public ImageSource(Context context, String source) { this(context, source, 0.0d, 0.0d); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK index 53e6a5a987f58..3eb01b3a51d6f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "modal", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -16,6 +17,8 @@ rn_android_library( YOGA_TARGET, react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/jsr-305:jsr-305"), + react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), + react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), @@ -24,7 +27,6 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/views/common:common"), react_native_target("java/com/facebook/react/views/view:view"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), react_native_target("res:modal"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index 35e1f8b27422b..d4f3394269c27 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -91,6 +91,12 @@ public void setHardwareAccelerated(ReactModalHostView view, boolean hardwareAcce view.setHardwareAccelerated(hardwareAccelerated); } + @Override + @ReactProp(name = "visible") + public void setVisible(ReactModalHostView view, boolean visible) { + // iOS only + } + @Override public void setPresentationStyle(ReactModalHostView view, @Nullable String value) {} @@ -104,7 +110,8 @@ public void setSupportedOrientations(ReactModalHostView view, @Nullable Readable public void setIdentifier(ReactModalHostView view, int value) {} @Override - protected void addEventEmitters(ThemedReactContext reactContext, final ReactModalHostView view) { + protected void addEventEmitters( + final ThemedReactContext reactContext, final ReactModalHostView view) { final EventDispatcher dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.getId()); if (dispatcher != null) { @@ -112,16 +119,19 @@ protected void addEventEmitters(ThemedReactContext reactContext, final ReactModa new ReactModalHostView.OnRequestCloseListener() { @Override public void onRequestClose(DialogInterface dialog) { - dispatcher.dispatchEvent(new RequestCloseEvent(view.getId())); + dispatcher.dispatchEvent( + new RequestCloseEvent(UIManagerHelper.getSurfaceId(reactContext), view.getId())); } }); view.setOnShowListener( new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { - dispatcher.dispatchEvent(new ShowEvent(view.getId())); + dispatcher.dispatchEvent( + new ShowEvent(UIManagerHelper.getSurfaceId(reactContext), view.getId())); } }); + view.setEventDispatcher(dispatcher); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index 030315a27e29d..4142dbba5a001 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -23,6 +23,7 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.annotation.UiThread; +import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.R; import com.facebook.react.bridge.GuardedRunnable; @@ -61,6 +62,8 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListener, FabricViewStateManager.HasFabricViewStateManager { + private static final String TAG = "ReactModalHost"; + // This listener is called when the user presses KeyEvent.KEYCODE_BACK // An event is then passed to JS which can either close or not close the Modal by setting the // visible property @@ -202,6 +205,10 @@ protected void setHardwareAccelerated(boolean hardwareAccelerated) { mPropertyRequiresNewDialog = true; } + void setEventDispatcher(EventDispatcher eventDispatcher) { + mHostView.setEventDispatcher(eventDispatcher); + } + @Override public void onHostResume() { // We show the dialog again when the host resumes @@ -240,6 +247,15 @@ protected void showOrUpdate() { // If the existing Dialog is currently up, we may need to redraw it or we may be able to update // the property without having to recreate the dialog if (mDialog != null) { + Context dialogContext = ContextUtils.findContextOfType(mDialog.getContext(), Activity.class); + // TODO(T85755791): remove after investigation + FLog.e( + TAG, + "Updating existing dialog with context: " + + dialogContext + + "@" + + dialogContext.hashCode()); + if (mPropertyRequiresNewDialog) { dismiss(); } else { @@ -265,6 +281,9 @@ protected void showOrUpdate() { WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + // TODO(T85755791): remove after investigation + FLog.e(TAG, "Creating new dialog from context: " + context + "@" + context.hashCode()); + mDialog.setContentView(getContentView()); updateProperties(); @@ -274,14 +293,11 @@ protected void showOrUpdate() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP) { - // We need to stop the BACK button from closing the dialog by default so we capture - // that - // event and instead inform JS so that it can make the decision as to whether or not - // to - // allow the back button to close the dialog. If it chooses to, it can just set - // visible - // to false on the Modal and the Modal will go away - if (keyCode == KeyEvent.KEYCODE_BACK) { + // We need to stop the BACK button and ESCAPE key from closing the dialog by default + // so we capture that event and instead inform JS so that it can make the decision as + // to whether or not to allow the back/escape key to close the dialog. If it chooses + // to, it can just set visible to false on the Modal and the Modal will go away + if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) { Assertions.assertNotNull( mOnRequestCloseListener, "setOnRequestCloseListener must be called by the manager"); @@ -289,9 +305,8 @@ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { return true; } else { // We redirect the rest of the key events to the current activity, since the - // activity - // expects to receive those events and react to them, ie. in the case of the dev - // menu + // activity expects to receive those events and react to them, ie. in the case of + // the dev menu Activity currentActivity = ((ReactContext) getContext()).getCurrentActivity(); if (currentActivity != null) { return currentActivity.onKeyUp(keyCode, event); @@ -393,6 +408,7 @@ static class DialogRootViewGroup extends ReactViewGroup private boolean hasAdjustedSize = false; private int viewWidth; private int viewHeight; + private EventDispatcher mEventDispatcher; private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); @@ -402,6 +418,10 @@ public DialogRootViewGroup(Context context) { super(context); } + private void setEventDispatcher(EventDispatcher eventDispatcher) { + mEventDispatcher = eventDispatcher; + } + @Override protected void onSizeChanged(final int w, final int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); @@ -447,7 +467,7 @@ public void updateState(final int width, final int height) { // Check incoming state values. If they're already the correct value, return early to prevent // infinite UpdateState/SetState loop. - ReadableMap currentState = getFabricViewStateManager().getState(); + ReadableMap currentState = getFabricViewStateManager().getStateData(); if (currentState != null) { float delta = (float) 0.9; float stateScreenHeight = @@ -494,13 +514,13 @@ private ReactContext getReactContext() { @Override public boolean onInterceptTouchEvent(MotionEvent event) { - mJSTouchDispatcher.handleTouchEvent(event, getEventDispatcher()); + mJSTouchDispatcher.handleTouchEvent(event, mEventDispatcher); return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { - mJSTouchDispatcher.handleTouchEvent(event, getEventDispatcher()); + mJSTouchDispatcher.handleTouchEvent(event, mEventDispatcher); super.onTouchEvent(event); // In case when there is no children interested in handling touch event, we return true from // the root view in order to receive subsequent events related to that gesture @@ -509,7 +529,7 @@ public boolean onTouchEvent(MotionEvent event) { @Override public void onChildStartedNativeGesture(MotionEvent androidEvent) { - mJSTouchDispatcher.onChildStartedNativeGesture(androidEvent, getEventDispatcher()); + mJSTouchDispatcher.onChildStartedNativeGesture(androidEvent, mEventDispatcher); } @Override @@ -518,11 +538,6 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { // even when some other view disallow that } - private EventDispatcher getEventDispatcher() { - ReactContext reactContext = getReactContext(); - return reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); - } - @Override public FabricViewStateManager getFabricViewStateManager() { return mFabricViewStateManager; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/RequestCloseEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/RequestCloseEvent.java index ba01778ed67a7..aa8ed464e570a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/RequestCloseEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/RequestCloseEvent.java @@ -7,16 +7,23 @@ package com.facebook.react.views.modal; +import androidx.annotation.Nullable; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** {@link Event} for dismissing a Dialog. */ /* package */ class RequestCloseEvent extends Event { public static final String EVENT_NAME = "topRequestClose"; + @Deprecated protected RequestCloseEvent(int viewTag) { - super(viewTag); + this(-1, viewTag); + } + + protected RequestCloseEvent(int surfaceId, int viewTag) { + super(surfaceId, viewTag); } @Override @@ -24,8 +31,9 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), null); + protected WritableMap getEventData() { + return Arguments.createMap(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ShowEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ShowEvent.java index cccbd34766862..1f126db46830d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ShowEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/modal/ShowEvent.java @@ -7,16 +7,23 @@ package com.facebook.react.views.modal; +import androidx.annotation.Nullable; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** {@link Event} for showing a Dialog. */ /* package */ class ShowEvent extends Event { public static final String EVENT_NAME = "topShow"; + @Deprecated protected ShowEvent(int viewTag) { - super(viewTag); + this(-1, viewTag); + } + + protected ShowEvent(int surfaceId, int viewTag) { + super(surfaceId, viewTag); } @Override @@ -24,8 +31,9 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), null); + protected WritableMap getEventData() { + return Arguments.createMap(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK deleted file mode 100644 index 00b41242092ac..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK +++ /dev/null @@ -1,29 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "picker", - srcs = glob(["**/*.java"]), - is_androidx = True, - labels = ["supermodule:xplat/default/public.react_native.infra"], - provided_deps = [ - react_native_dep("third-party/android/androidx:annotation"), - react_native_dep("third-party/android/androidx:appcompat"), - react_native_dep("third-party/android/androidx:core"), - react_native_dep("third-party/android/androidx:fragment"), - react_native_dep("third-party/android/androidx:legacy-support-core-ui"), - react_native_dep("third-party/android/androidx:legacy-support-core-utils"), - ], - visibility = [ - "PUBLIC", - ], - deps = [ - react_native_dep("third-party/java/infer-annotations:infer-annotations"), - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/common:common"), - react_native_target("java/com/facebook/react/module/annotations:annotations"), - react_native_target("java/com/facebook/react/uimanager:uimanager"), - react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), - ], -) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDialogPickerManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDialogPickerManager.java deleted file mode 100644 index 133dadf57a381..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDialogPickerManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker; - -import android.widget.Spinner; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewManagerDelegate; -import com.facebook.react.viewmanagers.AndroidDialogPickerManagerDelegate; -import com.facebook.react.viewmanagers.AndroidDialogPickerManagerInterface; - -/** {@link ReactPickerManager} for {@link ReactPicker} with {@link Spinner#MODE_DIALOG}. */ -@ReactModule(name = ReactDialogPickerManager.REACT_CLASS) -public class ReactDialogPickerManager extends ReactPickerManager - implements AndroidDialogPickerManagerInterface { - - public static final String REACT_CLASS = "AndroidDialogPicker"; - - private final ViewManagerDelegate mDelegate; - - public ReactDialogPickerManager() { - mDelegate = new AndroidDialogPickerManagerDelegate<>(this); - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - protected ReactPicker createViewInstance(ThemedReactContext reactContext) { - return new ReactPicker(reactContext, Spinner.MODE_DIALOG); - } - - @Override - protected ViewManagerDelegate getDelegate() { - return mDelegate; - } - - @Override - public void setBackgroundColor(@NonNull ReactPicker view, @Nullable Integer backgroundColor) { - view.setStagedBackgroundColor(backgroundColor); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDropdownPickerManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDropdownPickerManager.java deleted file mode 100644 index 04b3f3d341d69..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactDropdownPickerManager.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker; - -import android.widget.Spinner; -import androidx.annotation.NonNull; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewManagerDelegate; -import com.facebook.react.viewmanagers.AndroidDropdownPickerManagerDelegate; -import com.facebook.react.viewmanagers.AndroidDropdownPickerManagerInterface; - -/** {@link ReactPickerManager} for {@link ReactPicker} with {@link Spinner#MODE_DROPDOWN}. */ -@ReactModule(name = ReactDropdownPickerManager.REACT_CLASS) -public class ReactDropdownPickerManager extends ReactPickerManager - implements AndroidDropdownPickerManagerInterface { - - public static final String REACT_CLASS = "AndroidDropdownPicker"; - - private final ViewManagerDelegate mDelegate; - - public ReactDropdownPickerManager() { - mDelegate = new AndroidDropdownPickerManagerDelegate<>(this); - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - protected ReactPicker createViewInstance(ThemedReactContext reactContext) { - return new ReactPicker(reactContext, Spinner.MODE_DROPDOWN); - } - - @Override - protected ViewManagerDelegate getDelegate() { - return mDelegate; - } - - @Override - public void setBackgroundColor(@NonNull ReactPicker view, int backgroundColor) { - view.setStagedBackgroundColor(backgroundColor); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java deleted file mode 100644 index 8eff2dd4f7bf2..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.util.AttributeSet; -import android.view.View; -import android.widget.AdapterView; -import android.widget.Spinner; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatSpinner; -import androidx.core.view.ViewCompat; -import com.facebook.react.common.annotations.VisibleForTesting; -import java.util.List; - -public class ReactPicker extends AppCompatSpinner { - - private int mMode = Spinner.MODE_DIALOG; - private @Nullable OnSelectListener mOnSelectListener; - private @Nullable List mItems; - private @Nullable List mStagedItems; - private @Nullable Integer mStagedSelection; - private @Nullable Integer mStagedPrimaryTextColor; - private @Nullable Integer mStagedBackgroundColor; - - private final OnItemSelectedListener mItemSelectedListener = - new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - if (mOnSelectListener != null) { - mOnSelectListener.onItemSelected(position); - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - if (mOnSelectListener != null) { - mOnSelectListener.onItemSelected(-1); - } - } - }; - - /** Listener interface for ReactPicker events. */ - public interface OnSelectListener { - void onItemSelected(int position); - } - - public ReactPicker(Context context) { - super(context); - } - - public ReactPicker(Context context, int mode) { - super(context, mode); - mMode = mode; - } - - public ReactPicker(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ReactPicker(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public ReactPicker(Context context, AttributeSet attrs, int defStyle, int mode) { - super(context, attrs, defStyle, mode); - mMode = mode; - } - - private final Runnable measureAndLayout = - new Runnable() { - @Override - public void run() { - measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); - layout(getLeft(), getTop(), getRight(), getBottom()); - } - }; - - @Override - public void requestLayout() { - super.requestLayout(); - - // The spinner relies on a measure + layout pass happening after it calls requestLayout(). - // Without this, the widget never actually changes the selection and doesn't call the - // appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never - // happens after a call to requestLayout, so we simulate one here. - post(measureAndLayout); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - // onItemSelected gets fired immediately after layout because checkSelectionChanged() in - // AdapterView updates the selection position from the default INVALID_POSITION. - // To match iOS behavior, which no onItemSelected during initial layout. - // We setup the listener after layout. - if (getOnItemSelectedListener() == null) setOnItemSelectedListener(mItemSelectedListener); - } - - public void setOnSelectListener(@Nullable OnSelectListener onSelectListener) { - mOnSelectListener = onSelectListener; - } - - @Nullable - public OnSelectListener getOnSelectListener() { - return mOnSelectListener; - } - - /* package */ void setStagedItems(final @Nullable List items) { - mStagedItems = items; - } - - /** - * Will cache "selection" value locally and set it only once {@link #commitStagedData} is called - */ - /* package */ void setStagedSelection(int selection) { - mStagedSelection = selection; - } - - /** Will set the "selection" value immediately as opposed to {@link #setStagedSelection(int)} */ - /* package */ void setImmediateSelection(int selection) { - if (selection != getSelectedItemPosition()) { - setOnItemSelectedListener(null); - setSelection(selection, false); - setOnItemSelectedListener(mItemSelectedListener); - } - } - - /* package */ void setStagedPrimaryTextColor(@Nullable Integer primaryColor) { - mStagedPrimaryTextColor = primaryColor; - } - - /* package */ void setStagedBackgroundColor(@Nullable Integer backgroundColor) { - mStagedBackgroundColor = backgroundColor; - } - - /** - * Used to commit staged data into ReactPicker view. During this period, we will disable {@link - * OnSelectListener#onItemSelected(int)} temporarily, so we don't get an event when changing the - * items/selection ourselves. - */ - /* package */ void commitStagedData() { - setOnItemSelectedListener(null); - - ReactPickerAdapter adapter = (ReactPickerAdapter) getAdapter(); - final int origSelection = getSelectedItemPosition(); - if (mStagedItems != null && mStagedItems != mItems) { - mItems = mStagedItems; - mStagedItems = null; - if (adapter == null) { - adapter = new ReactPickerAdapter(getContext(), mItems); - setAdapter(adapter); - } else { - adapter.clear(); - adapter.addAll(mItems); - adapter.notifyDataSetChanged(); - } - } - - if (mStagedSelection != null && mStagedSelection != origSelection) { - setSelection(mStagedSelection, false); - mStagedSelection = null; - } - - if (mStagedPrimaryTextColor != null - && adapter != null - && mStagedPrimaryTextColor != adapter.getPrimaryTextColor()) { - adapter.setPrimaryTextColor(mStagedPrimaryTextColor); - ViewCompat.setBackgroundTintList(this, ColorStateList.valueOf(mStagedPrimaryTextColor)); - mStagedPrimaryTextColor = null; - } - - if (mStagedBackgroundColor != null - && adapter != null - && mStagedBackgroundColor != adapter.getBackgroundColor()) { - adapter.setBackgroundColor(mStagedBackgroundColor); - mStagedBackgroundColor = null; - } - - setOnItemSelectedListener(mItemSelectedListener); - } - - @VisibleForTesting - public int getMode() { - return mMode; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerAdapter.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerAdapter.java deleted file mode 100644 index 4f2890e79158c..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerAdapter.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Assertions; -import java.util.List; - -/* package */ -class ReactPickerAdapter extends ArrayAdapter { - - private final LayoutInflater mInflater; - private @Nullable Integer mPrimaryTextColor; - private @Nullable Integer mBackgroundColor; - - public ReactPickerAdapter(Context context, List data) { - super(context, 0, data); - - mInflater = - (LayoutInflater) - Assertions.assertNotNull(context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return getView(position, convertView, parent, false); - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - return getView(position, convertView, parent, true); - } - - private View getView(int position, View convertView, ViewGroup parent, boolean isDropdown) { - ReactPickerItem item = getItem(position); - boolean isNew = false; - if (convertView == null) { - int layoutResId = - isDropdown - ? android.R.layout.simple_spinner_dropdown_item - : android.R.layout.simple_spinner_item; - convertView = mInflater.inflate(layoutResId, parent, false); - // Save original text colors - convertView.setTag(((TextView) convertView).getTextColors()); - isNew = true; - } - - TextView textView = (TextView) convertView; - textView.setText(item.label); - if (!isDropdown && mPrimaryTextColor != null) { - textView.setTextColor(mPrimaryTextColor); - } else if (item.color != null) { - textView.setTextColor(item.color); - } else if (textView.getTag() != null && !isNew) { - // In case the new item does not set the color prop, go back to the default one - textView.setTextColor((ColorStateList) textView.getTag()); - } - - if (mBackgroundColor != null) { - textView.setBackgroundColor(mBackgroundColor); - } - - return textView; - } - - public @Nullable Integer getPrimaryTextColor() { - return mPrimaryTextColor; - } - - public @Nullable Integer getBackgroundColor() { - return mBackgroundColor; - } - - public void setPrimaryTextColor(@Nullable Integer primaryTextColor) { - mPrimaryTextColor = primaryTextColor; - notifyDataSetChanged(); - } - - public void setBackgroundColor(@Nullable Integer backgroundColor) { - mBackgroundColor = backgroundColor; - notifyDataSetChanged(); - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerItem.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerItem.java deleted file mode 100644 index 733deb3907022..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerItem.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker; - -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import java.util.ArrayList; -import java.util.List; - -/* package */ -class ReactPickerItem { - public final String label; - @Nullable public final Integer color; - - public ReactPickerItem(final ReadableMap jsMapData) { - label = jsMapData.getString("label"); - - if (jsMapData.hasKey("color") && !jsMapData.isNull("color")) { - color = jsMapData.getInt("color"); - } else { - color = null; - } - } - - @Nullable - public static List createFromJsArrayMap(final ReadableArray jsArrayMap) { - if (jsArrayMap == null) { - return null; - } - - final List items = new ArrayList<>(jsArrayMap.size()); - for (int i = 0; i < jsArrayMap.size(); ++i) { - items.add(new ReactPickerItem(jsArrayMap.getMap(i))); - } - return items; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java deleted file mode 100644 index ffc46232aa999..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker; - -import android.widget.Spinner; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.SimpleViewManager; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerHelper; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.views.picker.events.PickerItemSelectEvent; -import java.util.List; - -/** - * {@link ViewManager} for the {@link ReactPicker} view. This is abstract because the {@link - * Spinner} doesn't support setting the mode (dropdown/dialog) outside the constructor, so that is - * delegated to the separate {@link ReactDropdownPickerManager} and {@link ReactDialogPickerManager} - * components. These are merged back on the JS side into one React component. - */ -public abstract class ReactPickerManager extends SimpleViewManager { - - @ReactProp(name = "items") - public void setItems(ReactPicker view, @Nullable ReadableArray items) { - final List pickerItems = ReactPickerItem.createFromJsArrayMap(items); - view.setStagedItems(pickerItems); - } - - @ReactProp(name = ViewProps.COLOR, customType = "Color") - public void setColor(ReactPicker view, @Nullable Integer color) { - view.setStagedPrimaryTextColor(color); - } - - @ReactProp(name = "prompt") - public void setPrompt(ReactPicker view, @Nullable String prompt) { - view.setPrompt(prompt); - } - - @ReactProp(name = ViewProps.ENABLED, defaultBoolean = true) - public void setEnabled(ReactPicker view, boolean enabled) { - view.setEnabled(enabled); - } - - @ReactProp(name = "selected") - public void setSelected(ReactPicker view, int selected) { - view.setStagedSelection(selected); - } - - @Override - protected void onAfterUpdateTransaction(ReactPicker view) { - super.onAfterUpdateTransaction(view); - view.commitStagedData(); - } - - @Override - protected void addEventEmitters(final ThemedReactContext reactContext, final ReactPicker picker) { - picker.setOnSelectListener( - new PickerEventEmitter( - picker, UIManagerHelper.getEventDispatcherForReactTag(reactContext, picker.getId()))); - } - - @Override - public void receiveCommand( - @NonNull ReactPicker view, String commandId, @Nullable ReadableArray args) { - switch (commandId) { - case "setNativeSelectedPosition": - if (args != null) { - view.setImmediateSelection(args.getInt(0)); - break; - } - } - } - - private static class PickerEventEmitter implements ReactPicker.OnSelectListener { - - private final ReactPicker mReactPicker; - private final EventDispatcher mEventDispatcher; - - public PickerEventEmitter(ReactPicker reactPicker, EventDispatcher eventDispatcher) { - mReactPicker = reactPicker; - mEventDispatcher = eventDispatcher; - } - - @Override - public void onItemSelected(int position) { - mEventDispatcher.dispatchEvent(new PickerItemSelectEvent(mReactPicker.getId(), position)); - } - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/events/PickerItemSelectEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/events/PickerItemSelectEvent.java deleted file mode 100644 index c64ccda134a7f..0000000000000 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/picker/events/PickerItemSelectEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its 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.picker.events; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -public class PickerItemSelectEvent extends Event { - public static final String EVENT_NAME = "topSelect"; - - private final int mPosition; - - public PickerItemSelectEvent(int id, int position) { - super(id); - mPosition = position; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { - WritableMap eventData = Arguments.createMap(); - eventData.putInt("position", mPosition); - return eventData; - } -} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK index af7909efc1e8e..ca914a3c79652 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "progressbar", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -18,6 +19,6 @@ rn_android_library( react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), + react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK index 736a73dc53756..4cfccd38148a6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "scroll", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -29,5 +30,6 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/views/view:view"), + react_native_target("res:uimanager"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java index 6db6df6688c4a..831e4555a590e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java @@ -8,7 +8,6 @@ package com.facebook.react.views.scroll; import android.content.Context; -import android.widget.HorizontalScrollView; import androidx.core.view.ViewCompat; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.views.view.ReactViewGroup; @@ -28,6 +27,21 @@ public ReactHorizontalScrollContainerView(Context context) { mCurrentWidth = 0; } + @Override + public void setRemoveClippedSubviews(boolean removeClippedSubviews) { + // Clipping doesn't work well for horizontal scroll views in RTL mode - in both + // Fabric and non-Fabric - especially with TextInputs. The behavior you could see + // is TextInputs being blurred immediately after being focused. So, for now, + // it's easier to just disable this for these specific RTL views. + // TODO T86027499: support `setRemoveClippedSubviews` in RTL mode + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { + super.setRemoveClippedSubviews(false); + return; + } + + super.setRemoveClippedSubviews(removeClippedSubviews); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { @@ -39,11 +53,20 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto setLeft(newLeft); setRight(newRight); - // Call with the present values in order to re-layout if necessary - HorizontalScrollView parent = (HorizontalScrollView) getParent(); - // Fix the ScrollX position when using RTL language - int offsetX = parent.getScrollX() + getWidth() - mCurrentWidth; - parent.scrollTo(offsetX, parent.getScrollY()); + /** + * Note: in RTL mode, *when layout width changes*, we adjust the scroll position. Practically, + * this means that on the first (meaningful) layout we will go from position 0 to position + * (right - screenWidth). In theory this means if the width of the view ever changes during + * layout again, scrolling could jump. Which shouldn't happen in theory, but... if you find a + * weird product bug that looks related, keep this in mind. + */ + if (mCurrentWidth != getWidth()) { + // Call with the present values in order to re-layout if necessary + ReactHorizontalScrollView parent = (ReactHorizontalScrollView) getParent(); + // Fix the ScrollX position when using RTL language + int offsetX = parent.getScrollX() + getWidth() - mCurrentWidth - parent.getWidth(); + parent.scrollTo(offsetX, parent.getScrollY()); + } } mCurrentWidth = getWidth(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 57ed5ae6a3962..470c7d0f9bff7 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -7,6 +7,12 @@ package com.facebook.react.views.scroll; +import static com.facebook.react.config.ReactFeatureFlags.enableScrollViewSnapToAlignmentProp; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_CENTER; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -21,11 +27,11 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.HorizontalScrollView; import android.widget.OverScroller; import androidx.annotation.Nullable; -import androidx.core.text.TextUtilsCompat; import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; @@ -34,28 +40,40 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.ReactConstants; -import com.facebook.react.config.ReactFeatureFlags; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactClippingViewGroup; import com.facebook.react.uimanager.ReactClippingViewGroupHelper; +import com.facebook.react.uimanager.ReactOverflowView; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.views.view.ReactViewBackgroundManager; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; -import java.util.Locale; /** Similar to {@link ReactScrollView} but only supports horizontal scrolling. */ public class ReactHorizontalScrollView extends HorizontalScrollView - implements ReactClippingViewGroup, FabricViewStateManager.HasFabricViewStateManager { + implements ReactClippingViewGroup, + FabricViewStateManager.HasFabricViewStateManager, + ReactOverflowView { + + private static boolean DEBUG_MODE = false && ReactBuildConfig.DEBUG; + private static String TAG = ReactHorizontalScrollView.class.getSimpleName(); + + private static int NO_SCROLL_POSITION = Integer.MIN_VALUE; private static @Nullable Field sScrollerField; private static boolean sTriedToGetScrollerField = false; private static final String CONTENT_OFFSET_LEFT = "contentOffsetLeft"; private static final String CONTENT_OFFSET_TOP = "contentOffsetTop"; + private static final String SCROLL_AWAY_PADDING_TOP = "scrollAwayPaddingTop"; + private int mLayoutDirection; + + private int mScrollXAfterMeasure = NO_SCROLL_POSITION; private static final int UNSET_CONTENT_OFFSET = -1; @@ -83,6 +101,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private @Nullable List mSnapOffsets; private boolean mSnapToStart = true; private boolean mSnapToEnd = true; + private int mSnapToAlignment = SNAP_ALIGNMENT_DISABLED; private ReactViewBackgroundManager mReactBackgroundManager; private boolean mPagedArrowScrolling = false; private int pendingContentOffsetX = UNSET_CONTENT_OFFSET; @@ -125,6 +144,10 @@ public void onInitializeAccessibilityNodeInfo( }); mScroller = getOverScrollerFromParent(); + mLayoutDirection = + I18nUtil.getInstance().isRTL(context) + ? ViewCompat.LAYOUT_DIRECTION_RTL + : ViewCompat.LAYOUT_DIRECTION_LTR; } @Nullable @@ -138,7 +161,7 @@ private OverScroller getOverScrollerFromParent() { sScrollerField.setAccessible(true); } catch (NoSuchFieldException e) { FLog.w( - ReactConstants.TAG, + TAG, "Failed to get mScroller field for HorizontalScrollView! " + "This app will exhibit the bounce-back scrolling bug :("); } @@ -151,7 +174,7 @@ private OverScroller getOverScrollerFromParent() { scroller = (OverScroller) scrollerValue; } else { FLog.w( - ReactConstants.TAG, + TAG, "Failed to cast mScroller field in HorizontalScrollView (probably due to OEM changes to AOSP)! " + "This app will exhibit the bounce-back scrolling bug :("); scroller = null; @@ -224,6 +247,10 @@ public void setSnapToEnd(boolean snapToEnd) { mSnapToEnd = snapToEnd; } + public void setSnapToAlignment(int snapToAlignment) { + mSnapToAlignment = snapToAlignment; + } + public void flashScrollIndicators() { awakenScrollBars(); } @@ -233,8 +260,17 @@ public void setOverflow(String overflow) { invalidate(); } + @Override + public @Nullable String getOverflow() { + return mOverflow; + } + @Override protected void onDraw(Canvas canvas) { + if (DEBUG_MODE) { + FLog.i(TAG, "onDraw[%d]", getId()); + } + getDrawingRect(mRect); switch (mOverflow) { @@ -252,12 +288,59 @@ protected void onDraw(Canvas canvas) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { MeasureSpecAssertions.assertExplicitMeasureSpec(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension( - MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); + int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); + int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); + + if (DEBUG_MODE) { + FLog.i( + TAG, + "onMeasure[%d] measured width: %d measured height: %d", + getId(), + measuredWidth, + measuredHeight); + } + + boolean measuredHeightChanged = getMeasuredHeight() != measuredHeight; + + setMeasuredDimension(measuredWidth, measuredHeight); + + // See how `mScrollXAfterMeasure` is used in `onLayout`, and why we only enable the + // hack if the height has changed. + if (measuredHeightChanged && mScroller != null) { + mScrollXAfterMeasure = mScroller.getCurrX(); + } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { + if (DEBUG_MODE) { + FLog.i(TAG, "onLayout[%d] l %d t %d r %d b %d", getId(), l, t, r, b); + } + + // Has the scrollX changed between the last onMeasure and this layout? + // If so, cancel the animation. + // Essentially, if the height changes (due to keyboard popping up, for instance) the + // underlying View.layout method will in some cases scroll to an incorrect X position - + // see also the hacks in `fling`. The order of layout is called in the order of: onMeasure, + // layout, onLayout. + // We cannot override `layout` but we can detect the sequence of events between onMeasure + // and onLayout. + if (mScrollXAfterMeasure != NO_SCROLL_POSITION + && mScroller != null + && mScrollXAfterMeasure != mScroller.getFinalX() + && !mScroller.isFinished()) { + if (DEBUG_MODE) { + FLog.i( + TAG, + "onLayout[%d] scroll hack enabled: reset to previous scrollX position of %d", + getId(), + mScrollXAfterMeasure); + } + mScroller.startScroll(mScrollXAfterMeasure, mScroller.getFinalY(), 0, 0); + mScroller.forceFinished(true); + mScrollXAfterMeasure = NO_SCROLL_POSITION; + } + // Call with the present values in order to re-layout if necessary // If a "pending" value has been set, we restore that value. // That value gets cleared by reactScrollTo. @@ -265,7 +348,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX(); int scrollToY = pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); - reactScrollTo(scrollToX, scrollToY); + scrollTo(scrollToX, scrollToY); ReactScrollViewHelper.emitLayoutEvent(this); } @@ -341,6 +424,10 @@ private void scrollToChild(View child) { @Override protected void onScrollChanged(int x, int y, int oldX, int oldY) { + if (DEBUG_MODE) { + FLog.i(TAG, "onScrollChanged[%d] x %d y %d oldx %d oldy %d", getId(), x, y, oldX, oldY); + } + super.onScrollChanged(x, y, oldX, oldY); mActivelyScrolling = true; @@ -350,6 +437,12 @@ protected void onScrollChanged(int x, int y, int oldX, int oldY) { updateClippingRect(); } + // Race an UpdateState with every onScroll. This makes it more likely that, in Fabric, + // when JS processes the scroll event, the C++ ShadowNode representation will have a + // "more correct" scroll position. It will frequently be /incorrect/ but this decreases + // the error as much as possible. + updateStateOnScroll(); + ReactScrollViewHelper.emitScrollEvent( this, mOnScrollDispatchHelper.getXFlingVelocity(), @@ -402,7 +495,7 @@ public boolean arrowScroll(int direction) { if (getChildCount() > 0) { View currentFocused = findFocus(); View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); - View rootChild = getChildAt(0); + View rootChild = getContentView(); if (rootChild != null && nextFocused != null && nextFocused.getParent() == rootChild) { if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) { smoothScrollToNextPage(direction); @@ -459,6 +552,9 @@ public boolean executeKeyEvent(KeyEvent event) { @Override public void fling(int velocityX) { + if (DEBUG_MODE) { + FLog.i(TAG, "fling[%d] velocityX %d", getId(), velocityX); + } // Workaround. // On Android P if a ScrollView is inverted, we will get a wrong sign for @@ -533,7 +629,7 @@ public void updateClippingRect() { Assertions.assertNotNull(mClippingRect); ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect); - View contentView = getChildAt(0); + View contentView = getContentView(); if (contentView instanceof ReactClippingViewGroup) { ((ReactClippingViewGroup) contentView).updateClippingRect(); } @@ -546,9 +642,7 @@ public void getClippingRect(Rect outClippingRect) { @Override public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { - return ReactFeatureFlags.clipChildRectsIfOverflowIsHidden - ? ReactClippingViewGroupHelper.getChildVisibleRectHelper(child, r, offset, this, mOverflow) - : super.getChildVisibleRect(child, r, offset); + return super.getChildVisibleRect(child, r, offset); } private int getSnapInterval() { @@ -558,6 +652,11 @@ private int getSnapInterval() { return getWidth(); } + private View getContentView() { + View contentView = getChildAt(0); + return contentView; + } + public void setEndFillColor(int color) { if (color != mEndFillColor) { mEndFillColor = color; @@ -567,6 +666,17 @@ public void setEndFillColor(int color) { @Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { + if (DEBUG_MODE) { + FLog.i( + TAG, + "onOverScrolled[%d] scrollX %d scrollY %d clampedX %b clampedY %b", + getId(), + scrollX, + scrollY, + clampedX, + clampedY); + } + if (mScroller != null) { // FB SCROLLVIEW CHANGE @@ -611,7 +721,7 @@ private boolean isScrollPerfLoggingEnabled() { @Override public void draw(Canvas canvas) { if (mEndFillColor != Color.TRANSPARENT) { - final View content = getChildAt(0); + final View content = getContentView(); if (mEndBackground != null && content != null && content.getRight() < getWidth()) { mEndBackground.setBounds(content.getRight(), 0, getWidth(), getHeight()); mEndBackground.draw(canvas); @@ -627,6 +737,15 @@ public void draw(Canvas canvas) { * runnable that checks if we scrolled in the last frame and if so assumes we are still scrolling. */ private void handlePostTouchScrolling(int velocityX, int velocityY) { + if (DEBUG_MODE) { + FLog.i( + TAG, + "handlePostTouchScrolling[%d] velocityX %d velocityY %d", + getId(), + velocityX, + velocityY); + } + // Check if we are already handling this which may occur if this is called by both the touch up // and a fling call if (mPostTouchRunnable != null) { @@ -740,6 +859,10 @@ private int predictFinalScrollPosition(int velocityX) { * scrolling, and handling any momentum scrolling. */ private void smoothScrollAndSnap(int velocity) { + if (DEBUG_MODE) { + FLog.i(TAG, "smoothScrollAndSnap[%d] velocity %d", getId(), velocity); + } + double interval = (double) getSnapInterval(); double currentOffset = (double) (getPostAnimationScrollX()); double targetOffset = (double) predictFinalScrollPosition(velocity); @@ -785,12 +908,18 @@ private void smoothScrollAndSnap(int velocity) { } private void flingAndSnap(int velocityX) { + if (DEBUG_MODE) { + FLog.i(TAG, "smoothScrollAndSnap[%d] velocityX %d", getId(), velocityX); + } + if (getChildCount() <= 0) { return; } // pagingEnabled only allows snapping one interval at a time - if (mSnapInterval == 0 && mSnapOffsets == null) { + if (mSnapInterval == 0 + && mSnapOffsets == null + && (!enableScrollViewSnapToAlignmentProp || mSnapToAlignment == SNAP_ALIGNMENT_DISABLED)) { smoothScrollAndSnap(velocityX); return; } @@ -808,10 +937,7 @@ private void flingAndSnap(int velocityX) { int width = getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this); // offsets are from the right edge in RTL layouts - boolean isRTL = - TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) - == ViewCompat.LAYOUT_DIRECTION_RTL; - if (isRTL) { + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { targetOffset = maximumOffset - targetOffset; velocityX = -velocityX; } @@ -836,8 +962,46 @@ private void flingAndSnap(int velocityX) { } } } + } else if (enableScrollViewSnapToAlignmentProp && mSnapToAlignment != SNAP_ALIGNMENT_DISABLED) { + if (mSnapInterval > 0) { + double ratio = (double) targetOffset / mSnapInterval; + smallerOffset = + Math.max( + getItemStartOffset( + mSnapToAlignment, + (int) (Math.floor(ratio) * mSnapInterval), + mSnapInterval, + width), + 0); + largerOffset = + Math.min( + getItemStartOffset( + mSnapToAlignment, + (int) (Math.ceil(ratio) * mSnapInterval), + mSnapInterval, + width), + maximumOffset); + } else { + ViewGroup contentView = (ViewGroup) getContentView(); + for (int i = 1; i < contentView.getChildCount(); i++) { + View item = contentView.getChildAt(i); + int itemStartOffset = + getItemStartOffset(mSnapToAlignment, item.getLeft(), item.getWidth(), width); + if (itemStartOffset <= targetOffset) { + if (targetOffset - itemStartOffset < targetOffset - smallerOffset) { + smallerOffset = itemStartOffset; + } + } + + if (itemStartOffset >= targetOffset) { + if (itemStartOffset - targetOffset < largerOffset - targetOffset) { + largerOffset = itemStartOffset; + } + } + } + } } else { - double interval = (double) getSnapInterval(); + double interval = getSnapInterval(); double ratio = (double) targetOffset / interval; smallerOffset = (int) (Math.floor(ratio) * interval); largerOffset = Math.min((int) (Math.ceil(ratio) * interval), maximumOffset); @@ -850,7 +1014,7 @@ private void flingAndSnap(int velocityX) { // if scrolling after the last snap offset and snapping to the // end of the list is disabled, then we allow free scrolling int currentOffset = getScrollX(); - if (isRTL) { + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { currentOffset = maximumOffset - currentOffset; } if (!mSnapToEnd && targetOffset >= lastOffset) { @@ -884,7 +1048,7 @@ private void flingAndSnap(int velocityX) { // Make sure the new offset isn't out of bounds targetOffset = Math.min(Math.max(0, targetOffset), maximumOffset); - if (isRTL) { + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { targetOffset = maximumOffset - targetOffset; velocityX = -velocityX; } @@ -920,7 +1084,30 @@ private void flingAndSnap(int velocityX) { } } + private int getItemStartOffset( + int snapToAlignment, int itemStartPosition, int itemWidth, int viewPortWidth) { + int itemStartOffset; + switch (snapToAlignment) { + case SNAP_ALIGNMENT_CENTER: + itemStartOffset = itemStartPosition - (viewPortWidth - itemWidth) / 2; + break; + case SNAP_ALIGNMENT_START: + itemStartOffset = itemStartPosition; + break; + case SNAP_ALIGNMENT_END: + itemStartOffset = itemStartPosition - (viewPortWidth - itemWidth); + break; + default: + throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment); + } + return itemStartOffset; + } + private void smoothScrollToNextPage(int direction) { + if (DEBUG_MODE) { + FLog.i(TAG, "smoothScrollToNextPage[%d] direction %d", getId(), direction); + } + int width = getWidth(); int currentX = getScrollX(); @@ -975,6 +1162,10 @@ public void setBorderStyle(@Nullable String style) { * scroll view and state. Calling raw `smoothScrollTo` doesn't update state. */ public void reactSmoothScrollTo(int x, int y) { + if (DEBUG_MODE) { + FLog.i(TAG, "reactSmoothScrollTo[%d] x %d y %d", getId(), x, y); + } + // `smoothScrollTo` contains some logic that, if called multiple times in a short amount of // time, will treat all calls as part of the same animation and will not lengthen the duration // of the animation. This means that, for example, if the user is scrolling rapidly, multiple @@ -1025,15 +1216,27 @@ public void onAnimationRepeat(Animator animator) {} } /** - * Calls `reactScrollTo` and updates state. + * Calls `super.scrollTo` and updates state. + * + *

`super.scrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between + * scroll view and state. * - *

`reactScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between - * scroll view and state. Calling raw `reactScrollTo` doesn't update state. + *

Note that while we can override scrollTo, we *cannot* override `smoothScrollTo` because it + * is final. See `reactSmoothScrollTo`. */ - public void reactScrollTo(int x, int y) { - scrollTo(x, y); - updateStateOnScroll(x, y); - setPendingContentOffsets(x, y); + @Override + public void scrollTo(int x, int y) { + if (DEBUG_MODE) { + FLog.i(TAG, "scrollTo[%d] x %d y %d", getId(), x, y); + } + + super.scrollTo(x, y); + // The final scroll position might be different from (x, y). For example, we may need to scroll + // to the last item in the list, but that item cannot be move to the start position of the view. + final int actualX = getScrollX(); + final int actualY = getScrollY(); + updateStateOnScroll(actualX, actualY); + setPendingContentOffsets(actualX, actualY); } /** @@ -1044,7 +1247,10 @@ public void reactScrollTo(int x, int y) { * @param y */ private void setPendingContentOffsets(int x, int y) { - View child = getChildAt(0); + if (DEBUG_MODE) { + FLog.i(TAG, "setPendingContentOffsets[%d] x %d y %d", getId(), x, y); + } + View child = getContentView(); if (child != null && child.getWidth() != 0 && child.getHeight() != 0) { pendingContentOffsetX = UNSET_CONTENT_OFFSET; pendingContentOffsetY = UNSET_CONTENT_OFFSET; @@ -1058,6 +1264,10 @@ private void setPendingContentOffsets(int x, int y) { * Called on any stabilized onScroll change to propagate content offset value to a Shadow Node. */ private void updateStateOnScroll(final int scrollX, final int scrollY) { + if (DEBUG_MODE) { + FLog.i(TAG, "updateStateOnScroll[%d] scrollX %d scrollY %d", getId(), scrollX, scrollY); + } + // Dedupe events to reduce JNI traffic if (scrollX == mLastStateUpdateScrollX && scrollY == mLastStateUpdateScrollY) { return; @@ -1066,13 +1276,35 @@ private void updateStateOnScroll(final int scrollX, final int scrollY) { mLastStateUpdateScrollX = scrollX; mLastStateUpdateScrollY = scrollY; + final int fabricScrollX; + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { + // getScrollX returns offset from left even when layout direction is RTL. + // The following line calculates offset from right. + View child = getContentView(); + int contentWidth = child != null ? child.getWidth() : 0; + fabricScrollX = -(contentWidth - scrollX - getWidth()); + } else { + fabricScrollX = scrollX; + } + + if (DEBUG_MODE) { + FLog.i( + TAG, + "updateStateOnScroll[%d] scrollX %d scrollY %d fabricScrollX", + getId(), + scrollX, + scrollY, + fabricScrollX); + } + mFabricViewStateManager.setState( new FabricViewStateManager.StateUpdateCallback() { @Override public WritableMap getStateUpdate() { WritableMap map = new WritableNativeMap(); - map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); + map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(fabricScrollX)); map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); + map.putDouble(SCROLL_AWAY_PADDING_TOP, 0); return map; } }); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index 9641fc11be9cf..5775d3106c703 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -102,9 +102,19 @@ public void setSnapToInterval(ReactHorizontalScrollView view, float snapToInterv view.setSnapInterval((int) (snapToInterval * screenDisplayMetrics.density)); } + @ReactProp(name = "snapToAlignment") + public void setSnapToAlignment(ReactHorizontalScrollView view, String alignment) { + view.setSnapToAlignment(ReactScrollViewHelper.parseSnapToAlignment(alignment)); + } + @ReactProp(name = "snapToOffsets") public void setSnapToOffsets( ReactHorizontalScrollView view, @Nullable ReadableArray snapToOffsets) { + if (snapToOffsets == null) { + view.setSnapOffsets(null); + return; + } + DisplayMetrics screenDisplayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics(); List offsets = new ArrayList(); for (int i = 0; i < snapToOffsets.size(); i++) { @@ -193,7 +203,7 @@ public void scrollTo( if (data.mAnimated) { scrollView.reactSmoothScrollTo(data.mDestX, data.mDestY); } else { - scrollView.reactScrollTo(data.mDestX, data.mDestY); + scrollView.scrollTo(data.mDestX, data.mDestY); } } @@ -206,7 +216,7 @@ public void scrollToEnd( if (data.mAnimated) { scrollView.reactSmoothScrollTo(right, scrollView.getScrollY()); } else { - scrollView.reactScrollTo(right, scrollView.getScrollY()); + scrollView.scrollTo(right, scrollView.getScrollY()); } } @@ -306,9 +316,9 @@ public void setContentOffset(ReactHorizontalScrollView view, ReadableMap value) if (value != null) { double x = value.hasKey("x") ? value.getDouble("x") : 0; double y = value.hasKey("y") ? value.getDouble("y") : 0; - view.reactScrollTo((int) PixelUtil.toPixelFromDIP(x), (int) PixelUtil.toPixelFromDIP(y)); + view.scrollTo((int) PixelUtil.toPixelFromDIP(x), (int) PixelUtil.toPixelFromDIP(y)); } else { - view.reactScrollTo(0, 0); + view.scrollTo(0, 0); } } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 118579c886f10..55b1ff495a247 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -7,6 +7,12 @@ package com.facebook.react.views.scroll; +import static com.facebook.react.config.ReactFeatureFlags.enableScrollViewSnapToAlignmentProp; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_CENTER; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END; +import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -20,23 +26,27 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.OverScroller; import android.widget.ScrollView; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.R; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.ReactConstants; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactClippingViewGroup; import com.facebook.react.uimanager.ReactClippingViewGroupHelper; +import com.facebook.react.uimanager.ReactOverflowView; import com.facebook.react.uimanager.ViewProps; +import com.facebook.react.uimanager.common.UIManagerType; +import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.views.view.ReactViewBackgroundManager; import java.lang.reflect.Field; @@ -53,12 +63,14 @@ public class ReactScrollView extends ScrollView implements ReactClippingViewGroup, ViewGroup.OnHierarchyChangeListener, View.OnLayoutChangeListener, - FabricViewStateManager.HasFabricViewStateManager { + FabricViewStateManager.HasFabricViewStateManager, + ReactOverflowView { private static @Nullable Field sScrollerField; private static boolean sTriedToGetScrollerField = false; private static final String CONTENT_OFFSET_LEFT = "contentOffsetLeft"; private static final String CONTENT_OFFSET_TOP = "contentOffsetTop"; + private static final String SCROLL_AWAY_PADDING_TOP = "scrollAwayPaddingTop"; private static final int UNSET_CONTENT_OFFSET = -1; @@ -86,6 +98,7 @@ public class ReactScrollView extends ScrollView private @Nullable List mSnapOffsets; private boolean mSnapToStart = true; private boolean mSnapToEnd = true; + private int mSnapToAlignment = SNAP_ALIGNMENT_DISABLED; private @Nullable View mContentView; private ReactViewBackgroundManager mReactBackgroundManager; private int pendingContentOffsetX = UNSET_CONTENT_OFFSET; @@ -96,6 +109,8 @@ public class ReactScrollView extends ScrollView private int mFinalAnimatedPositionScrollX; private int mFinalAnimatedPositionScrollY; + private int mScrollAwayPaddingTop = 0; + private int mLastStateUpdateScrollX = -1; private int mLastStateUpdateScrollY = -1; @@ -113,6 +128,20 @@ public ReactScrollView(ReactContext context, @Nullable FpsListener fpsListener) setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY); } + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + + // Expose the testID prop as the resource-id name of the view. Black-box E2E/UI testing + // frameworks, which interact with the UI through the accessibility framework, do not have + // access to view tags. This allows developers/testers to avoid polluting the + // content-description with test identifiers. + final String testId = (String) this.getTag(R.id.reactandroid_react_test_id); + if (testId != null) { + info.setViewIdResourceName(testId); + } + } + @Nullable private OverScroller getOverScrollerFromParent() { OverScroller scroller; @@ -196,6 +225,10 @@ public void setSnapToEnd(boolean snapToEnd) { mSnapToEnd = snapToEnd; } + public void setSnapToAlignment(int snapToAlignment) { + mSnapToAlignment = snapToAlignment; + } + public void flashScrollIndicators() { awakenScrollBars(); } @@ -205,6 +238,11 @@ public void setOverflow(String overflow) { invalidate(); } + @Override + public @Nullable String getOverflow() { + return mOverflow; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { MeasureSpecAssertions.assertExplicitMeasureSpec(widthMeasureSpec, heightMeasureSpec); @@ -222,7 +260,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX(); int scrollToY = pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); - reactScrollTo(scrollToX, scrollToY); + scrollTo(scrollToX, scrollToY); ReactScrollViewHelper.emitLayoutEvent(this); } @@ -282,6 +320,12 @@ protected void onScrollChanged(int x, int y, int oldX, int oldY) { updateClippingRect(); } + // Race an UpdateState with every onScroll. This makes it more likely that, in Fabric, + // when JS processes the scroll event, the C++ ShadowNode representation will have a + // "more correct" scroll position. It will frequently be /incorrect/ but this decreases + // the error as much as possible. + updateStateOnScroll(); + ReactScrollViewHelper.emitScrollEvent( this, mOnScrollDispatchHelper.getXFlingVelocity(), @@ -383,9 +427,7 @@ public void getClippingRect(Rect outClippingRect) { @Override public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { - return ReactFeatureFlags.clipChildRectsIfOverflowIsHidden - ? ReactClippingViewGroupHelper.getChildVisibleRectHelper(child, r, offset, this, mOverflow) - : super.getChildVisibleRect(child, r, offset); + return super.getChildVisibleRect(child, r, offset); } @Override @@ -603,6 +645,10 @@ private int predictFinalScrollPosition(int velocityY) { return scroller.getFinalY(); } + private View getContentView() { + return getChildAt(0); + } + /** * This will smooth scroll us to the nearest snap offset point It currently just looks at where * the content is and slides to the nearest point. It is intended to be run after we are done @@ -659,7 +705,9 @@ private void flingAndSnap(int velocityY) { } // pagingEnabled only allows snapping one interval at a time - if (mSnapInterval == 0 && mSnapOffsets == null) { + if (mSnapInterval == 0 + && mSnapOffsets == null + && (!enableScrollViewSnapToAlignmentProp || mSnapToAlignment == SNAP_ALIGNMENT_DISABLED)) { smoothScrollAndSnap(velocityY); return; } @@ -696,6 +744,57 @@ private void flingAndSnap(int velocityY) { } } } + + } else if (enableScrollViewSnapToAlignmentProp && mSnapToAlignment != SNAP_ALIGNMENT_DISABLED) { + if (mSnapInterval > 0) { + double ratio = (double) targetOffset / mSnapInterval; + smallerOffset = + Math.max( + getItemStartOffset( + mSnapToAlignment, + (int) (Math.floor(ratio) * mSnapInterval), + mSnapInterval, + height), + 0); + largerOffset = + Math.min( + getItemStartOffset( + mSnapToAlignment, + (int) (Math.ceil(ratio) * mSnapInterval), + mSnapInterval, + height), + maximumOffset); + } else { + ViewGroup contentView = (ViewGroup) getContentView(); + for (int i = 1; i < contentView.getChildCount(); i++) { + View item = contentView.getChildAt(i); + int itemStartOffset; + switch (mSnapToAlignment) { + case SNAP_ALIGNMENT_CENTER: + itemStartOffset = item.getTop() - (height - item.getHeight()) / 2; + break; + case SNAP_ALIGNMENT_START: + itemStartOffset = item.getTop(); + break; + case SNAP_ALIGNMENT_END: + itemStartOffset = item.getTop() - (height - item.getHeight()); + break; + default: + throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment); + } + if (itemStartOffset <= targetOffset) { + if (targetOffset - itemStartOffset < targetOffset - smallerOffset) { + smallerOffset = itemStartOffset; + } + } + + if (itemStartOffset >= targetOffset) { + if (itemStartOffset - targetOffset < largerOffset - targetOffset) { + largerOffset = itemStartOffset; + } + } + } + } } else { double interval = (double) getSnapInterval(); double ratio = (double) targetOffset / interval; @@ -771,6 +870,25 @@ private void flingAndSnap(int velocityY) { } } + private int getItemStartOffset( + int snapToAlignment, int itemStartPosition, int itemHeight, int viewPortHeight) { + int itemStartOffset; + switch (snapToAlignment) { + case SNAP_ALIGNMENT_CENTER: + itemStartOffset = itemStartPosition - (viewPortHeight - itemHeight) / 2; + break; + case SNAP_ALIGNMENT_START: + itemStartOffset = itemStartPosition; + break; + case SNAP_ALIGNMENT_END: + itemStartOffset = itemStartPosition - (viewPortHeight - itemHeight); + break; + default: + throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment); + } + return itemStartOffset; + } + private int getSnapInterval() { if (mSnapInterval != 0) { return mSnapInterval; @@ -881,11 +999,19 @@ public void onAnimationRepeat(Animator animator) {} * *

`reactScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between * scroll view and state. Calling raw `reactScrollTo` doesn't update state. + * + *

Note that while we can override scrollTo, we *cannot* override `smoothScrollTo` because it + * is final. See `reactSmoothScrollTo`. */ - public void reactScrollTo(int x, int y) { - scrollTo(x, y); - updateStateOnScroll(x, y); - setPendingContentOffsets(x, y); + @Override + public void scrollTo(int x, int y) { + super.scrollTo(x, y); + // The final scroll position might be different from (x, y). For example, we may need to scroll + // to the last item in the list, but that item cannot be move to the start position of the view. + final int actualX = getScrollX(); + final int actualY = getScrollY(); + updateStateOnScroll(actualX, actualY); + setPendingContentOffsets(actualX, actualY); } /** @@ -929,7 +1055,7 @@ public void onLayoutChange( int currentScrollY = getScrollY(); int maxScrollY = getMaxScrollY(); if (currentScrollY > maxScrollY) { - reactScrollTo(getScrollX(), maxScrollY); + scrollTo(getScrollX(), maxScrollY); } } @@ -958,35 +1084,94 @@ public void setBorderStyle(@Nullable String style) { mReactBackgroundManager.setBorderStyle(style); } + /** + * ScrollAway: This enables a natively-controlled navbar that optionally obscures the top content + * of the ScrollView. Whether or not the navbar is obscuring the React Native surface is + * determined outside of React Native. + * + *

Note: all ScrollViews and HorizontalScrollViews in React have exactly one child: the + * "content" View (see ScrollView.js). That View is non-collapsable so it will never be + * View-flattened away. However, it is possible to pass custom styles into that View. + * + *

If you are using this feature it is assumed that you have full control over this ScrollView + * and that you are **not** overriding the ScrollView content view to pass in a `translateY` + * style. `translateY` must never be set from ReactJS while using this feature! + */ + public void setScrollAwayTopPaddingEnabledUnstable(int topPadding) { + int count = getChildCount(); + + Assertions.assertCondition( + count == 1, "React Native ScrollView always has exactly 1 child; a content View"); + + if (count > 0) { + for (int i = 0; i < count; i++) { + View childView = getChildAt(i); + childView.setTranslationY(topPadding); + } + + // Add the topPadding value as the bottom padding for the ScrollView. + // Otherwise, we'll push down the contents of the scroll view down too + // far off screen. + setPadding(0, 0, 0, topPadding); + } + + updateScrollAwayState(topPadding); + setRemoveClippedSubviews(mRemoveClippedSubviews); + } + /** * Called on any stabilized onScroll change to propagate content offset value to a Shadow Node. */ - private void updateStateOnScroll(final int scrollX, final int scrollY) { + private boolean updateStateOnScroll(final int scrollX, final int scrollY) { + if (ViewUtil.getUIManagerType(getId()) == UIManagerType.DEFAULT) { + return false; + } + // Dedupe events to reduce JNI traffic if (scrollX == mLastStateUpdateScrollX && scrollY == mLastStateUpdateScrollY) { - return; + return false; } mLastStateUpdateScrollX = scrollX; mLastStateUpdateScrollY = scrollY; + forceUpdateState(); + + return true; + } + + private boolean updateStateOnScroll() { + return updateStateOnScroll(getScrollX(), getScrollY()); + } + + private void updateScrollAwayState(int scrollAwayPaddingTop) { + if (mScrollAwayPaddingTop == scrollAwayPaddingTop) { + return; + } + + mScrollAwayPaddingTop = scrollAwayPaddingTop; + + forceUpdateState(); + } + + private void forceUpdateState() { + final int scrollX = mLastStateUpdateScrollX; + final int scrollY = mLastStateUpdateScrollY; + final int scrollAwayPaddingTop = mScrollAwayPaddingTop; + mFabricViewStateManager.setState( new FabricViewStateManager.StateUpdateCallback() { @Override public WritableMap getStateUpdate() { - WritableMap map = new WritableNativeMap(); map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); + map.putDouble(SCROLL_AWAY_PADDING_TOP, PixelUtil.toDIPFromPixel(scrollAwayPaddingTop)); return map; } }); } - private void updateStateOnScroll() { - updateStateOnScroll(getScrollX(), getScrollY()); - } - @Override public FabricViewStateManager getFabricViewStateManager() { return mFabricViewStateManager; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index c10a38ebdb02a..f383b2931260c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -11,11 +11,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.OverScroller; +import androidx.annotation.Nullable; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.UIManagerHelper; -import java.util.ArrayList; -import java.util.List; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; /** Helper class that deals with emitting Scroll Events. */ public class ReactScrollViewHelper { @@ -25,6 +27,11 @@ public class ReactScrollViewHelper { public static final String AUTO = "auto"; public static final String OVER_SCROLL_NEVER = "never"; + public static final int SNAP_ALIGNMENT_DISABLED = 0; + public static final int SNAP_ALIGNMENT_START = 1; + public static final int SNAP_ALIGNMENT_CENTER = 2; + public static final int SNAP_ALIGNMENT_END = 3; + public interface ScrollListener { void onScroll( ViewGroup scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity); @@ -33,7 +40,8 @@ void onScroll( } // Support global native listeners for scroll events - private static List sScrollListeners = new ArrayList<>(); + private static final Set sScrollListeners = + Collections.newSetFromMap(new WeakHashMap()); // If all else fails, this is the hardcoded value in OverScroller.java, in AOSP. // The default is defined here (as of this diff): @@ -81,9 +89,11 @@ private static void emitScrollEvent( } ReactContext reactContext = (ReactContext) scrollView.getContext(); + int surfaceId = UIManagerHelper.getSurfaceId(reactContext); UIManagerHelper.getEventDispatcherForReactTag(reactContext, scrollView.getId()) .dispatchEvent( ScrollEvent.obtain( + surfaceId, scrollView.getId(), scrollEventType, scrollView.getScrollX(), @@ -115,6 +125,20 @@ public static int parseOverScrollMode(String jsOverScrollMode) { } } + public static int parseSnapToAlignment(@Nullable String alignment) { + if (alignment == null) { + return SNAP_ALIGNMENT_DISABLED; + } else if ("start".equalsIgnoreCase(alignment)) { + return SNAP_ALIGNMENT_START; + } else if ("center".equalsIgnoreCase(alignment)) { + return SNAP_ALIGNMENT_CENTER; + } else if ("end".equals(alignment)) { + return SNAP_ALIGNMENT_END; + } else { + throw new JSApplicationIllegalArgumentException("wrong snap alignment value: " + alignment); + } + } + public static int getDefaultScrollAnimationDuration(Context context) { if (!mSmoothScrollDurationInitialized) { mSmoothScrollDurationInitialized = true; @@ -154,6 +178,19 @@ public void startScroll(int startX, int startY, int dx, int dy, int duration) { } } + /** + * Adds a scroll listener. + * + *

Note that you must keep a reference to this scroll listener because this class only keeps a + * weak reference to it (to prevent memory leaks). This means that code like + * addScrollListener(new ScrollListener() {...}) won't work, you need to do this instead: + * + * mScrollListener = new ScrollListener() {...}; + * ReactScrollViewHelper.addScrollListener(mScrollListener); + * instead. + * + * @param listener + */ public static void addScrollListener(ScrollListener listener) { sScrollListeners.add(listener); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index 23ad78a5a880a..ac9787e56f79c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -102,6 +102,11 @@ public void setSnapToInterval(ReactScrollView view, float snapToInterval) { @ReactProp(name = "snapToOffsets") public void setSnapToOffsets(ReactScrollView view, @Nullable ReadableArray snapToOffsets) { + if (snapToOffsets == null) { + view.setSnapOffsets(null); + return; + } + DisplayMetrics screenDisplayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics(); List offsets = new ArrayList(); for (int i = 0; i < snapToOffsets.size(); i++) { @@ -110,6 +115,11 @@ public void setSnapToOffsets(ReactScrollView view, @Nullable ReadableArray snapT view.setSnapOffsets(offsets); } + @ReactProp(name = "snapToAlignment") + public void setSnapToAlignment(ReactScrollView view, String alignment) { + view.setSnapToAlignment(ReactScrollViewHelper.parseSnapToAlignment(alignment)); + } + @ReactProp(name = "snapToStart") public void setSnapToStart(ReactScrollView view, boolean snapToStart) { view.setSnapToStart(snapToStart); @@ -206,7 +216,7 @@ public void scrollTo( if (data.mAnimated) { scrollView.reactSmoothScrollTo(data.mDestX, data.mDestY); } else { - scrollView.reactScrollTo(data.mDestX, data.mDestY); + scrollView.scrollTo(data.mDestX, data.mDestY); } } @@ -285,7 +295,7 @@ public void scrollToEnd( if (data.mAnimated) { scrollView.reactSmoothScrollTo(scrollView.getScrollX(), bottom); } else { - scrollView.reactScrollTo(scrollView.getScrollX(), bottom); + scrollView.scrollTo(scrollView.getScrollX(), bottom); } } @@ -305,14 +315,14 @@ public void setFadingEdgeLength(ReactScrollView view, int value) { } } - @ReactProp(name = "contentOffset") + @ReactProp(name = "contentOffset", customType = "Point") public void setContentOffset(ReactScrollView view, ReadableMap value) { if (value != null) { double x = value.hasKey("x") ? value.getDouble("x") : 0; double y = value.hasKey("y") ? value.getDouble("y") : 0; - view.reactScrollTo((int) PixelUtil.toPixelFromDIP(x), (int) PixelUtil.toPixelFromDIP(y)); + view.scrollTo((int) PixelUtil.toPixelFromDIP(x), (int) PixelUtil.toPixelFromDIP(y)); } else { - view.reactScrollTo(0, 0); + view.scrollTo(0, 0); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.java index 91876fc2e3b90..ecefb6067b007 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.java @@ -11,13 +11,14 @@ import androidx.core.util.Pools; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** A event dispatched from a ScrollView scrolling. */ public class ScrollEvent extends Event { + private static String TAG = ScrollEvent.class.getSimpleName(); private static final Pools.SynchronizedPool EVENTS_POOL = new Pools.SynchronizedPool<>(3); @@ -32,6 +33,7 @@ public class ScrollEvent extends Event { private int mScrollViewHeight; private @Nullable ScrollEventType mScrollEventType; + @Deprecated public static ScrollEvent obtain( int viewTag, ScrollEventType scrollEventType, @@ -43,11 +45,38 @@ public static ScrollEvent obtain( int contentHeight, int scrollViewWidth, int scrollViewHeight) { + return obtain( + -1, + viewTag, + scrollEventType, + scrollX, + scrollY, + xVelocity, + yVelocity, + contentWidth, + contentHeight, + scrollViewWidth, + scrollViewHeight); + } + + public static ScrollEvent obtain( + int surfaceId, + int viewTag, + ScrollEventType scrollEventType, + int scrollX, + int scrollY, + float xVelocity, + float yVelocity, + int contentWidth, + int contentHeight, + int scrollViewWidth, + int scrollViewHeight) { ScrollEvent event = EVENTS_POOL.acquire(); if (event == null) { event = new ScrollEvent(); } event.init( + surfaceId, viewTag, scrollEventType, scrollX, @@ -63,12 +92,19 @@ public static ScrollEvent obtain( @Override public void onDispose() { - EVENTS_POOL.release(this); + try { + EVENTS_POOL.release(this); + } catch (IllegalStateException e) { + // This exception can be thrown when an event is double-released. + // This is a problem but won't cause user-visible impact, so it's okay to fail silently. + ReactSoftExceptionLogger.logSoftException(TAG, e); + } } private ScrollEvent() {} private void init( + int surfaceId, int viewTag, ScrollEventType scrollEventType, int scrollX, @@ -79,7 +115,7 @@ private void init( int contentHeight, int scrollViewWidth, int scrollViewHeight) { - super.init(viewTag); + super.init(surfaceId, viewTag); mScrollEventType = scrollEventType; mScrollX = scrollX; mScrollY = scrollY; @@ -96,12 +132,6 @@ public String getEventName() { return ScrollEventType.getJSEventName(Assertions.assertNotNull(mScrollEventType)); } - @Override - public short getCoalescingKey() { - // All scroll events for a given view can be coalesced - return 0; - } - @Override public boolean canCoalesce() { // Only SCROLL events can be coalesced, all others can not be @@ -111,12 +141,9 @@ public boolean canCoalesce() { return false; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap contentInset = Arguments.createMap(); contentInset.putDouble("top", 0); contentInset.putDouble("bottom", 0); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK index 63f1658e470b6..46109dc64b3fb 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "slider", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -21,6 +22,6 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), + react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java index 09f764ddb3753..5b5c85f85925f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderEvent.java @@ -39,11 +39,6 @@ public String getEventName() { return EVENT_NAME; } - @Override - public short getCoalescingKey() { - return 0; - } - @Override public void dispatch(RCTEventEmitter rctEventEmitter) { rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java index 9c8bc9ced0bca..278972551ef99 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java @@ -16,7 +16,6 @@ import android.view.ViewGroup; import android.widget.SeekBar; import androidx.annotation.Nullable; -import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import com.facebook.react.bridge.ReactContext; @@ -24,12 +23,14 @@ import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.uimanager.ReactAccessibilityDelegate; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.ViewManagerDelegate; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.annotations.ReactProp; +import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.viewmanagers.SliderManagerDelegate; import com.facebook.react.viewmanagers.SliderManagerInterface; import com.facebook.yoga.YogaMeasureFunction; @@ -95,16 +96,13 @@ public long measure( @Override public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) { ReactContext reactContext = (ReactContext) seekbar.getContext(); - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); - - if (uiManager != null) { - uiManager - .getEventDispatcher() - .dispatchEvent( - new ReactSliderEvent( - seekbar.getId(), - ((ReactSlider) seekbar).toRealProgress(progress), - fromUser)); + EventDispatcher eventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag(reactContext, seekbar.getId()); + + if (eventDispatcher != null) { + eventDispatcher.dispatchEvent( + new ReactSliderEvent( + seekbar.getId(), ((ReactSlider) seekbar).toRealProgress(progress), fromUser)); } } @@ -114,15 +112,15 @@ public void onStartTrackingTouch(SeekBar seekbar) {} @Override public void onStopTrackingTouch(SeekBar seekbar) { ReactContext reactContext = (ReactContext) seekbar.getContext(); - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); - - if (uiManager != null) { - uiManager - .getEventDispatcher() - .dispatchEvent( - new ReactSlidingCompleteEvent( - seekbar.getId(), - ((ReactSlider) seekbar).toRealProgress(seekbar.getProgress()))); + EventDispatcher eventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag(reactContext, seekbar.getId()); + + if (eventDispatcher != null) { + eventDispatcher.dispatchEvent( + new ReactSlidingCompleteEvent( + UIManagerHelper.getSurfaceId(seekbar), + seekbar.getId(), + ((ReactSlider) seekbar).toRealProgress(seekbar.getProgress()))); } } }; @@ -151,7 +149,7 @@ public Class getShadowNodeClass() { @Override protected ReactSlider createViewInstance(ThemedReactContext context) { final ReactSlider slider = new ReactSlider(context, null, STYLE); - ViewCompat.setAccessibilityDelegate(slider, sAccessibilityDelegate); + ViewCompat.setAccessibilityDelegate(slider, new ReactSliderAccessibilityDelegate()); return slider; } @@ -280,8 +278,8 @@ protected ViewManagerDelegate getDelegate() { return mDelegate; } - protected static class ReactSliderAccessibilityDelegate extends AccessibilityDelegateCompat { - private static boolean isSliderAction(int action) { + protected class ReactSliderAccessibilityDelegate extends ReactAccessibilityDelegate { + private boolean isSliderAction(int action) { return (action == AccessibilityActionCompat.ACTION_SCROLL_FORWARD.getId()) || (action == AccessibilityActionCompat.ACTION_SCROLL_BACKWARD.getId()) || (action == AccessibilityActionCompat.ACTION_SET_PROGRESS.getId()); @@ -299,7 +297,4 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) { return rv; } }; - - protected static ReactSliderAccessibilityDelegate sAccessibilityDelegate = - new ReactSliderAccessibilityDelegate(); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java index 2e2e9470707a8..44832295f071c 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSlidingCompleteEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.slider; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted when the user finishes dragging the slider. */ public class ReactSlidingCompleteEvent extends Event { @@ -19,8 +19,13 @@ public class ReactSlidingCompleteEvent extends Event private final double mValue; + @Deprecated public ReactSlidingCompleteEvent(int viewId, double value) { - super(viewId); + this(-1, viewId, value); + } + + public ReactSlidingCompleteEvent(int surfaceId, int viewId, double value) { + super(surfaceId, viewId); mValue = value; } @@ -33,25 +38,17 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public short getCoalescingKey() { - return 0; + protected WritableMap getEventData() { + WritableMap eventData = Arguments.createMap(); + eventData.putInt("target", getViewTag()); + eventData.putDouble("value", getValue()); + return eventData; } @Override public boolean canCoalesce() { return false; } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { - WritableMap eventData = Arguments.createMap(); - eventData.putInt("target", getViewTag()); - eventData.putDouble("value", getValue()); - return eventData; - } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK index 528c4af8a5505..73fe27d801b54 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "swiperefresh", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -25,6 +26,6 @@ rn_android_library( react_native_target("java/com/facebook/react/views/scroll:scroll"), ], exported_deps = [ - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), + react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.java index ca012572e71db..85d23cff992d5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.java @@ -7,13 +7,20 @@ package com.facebook.react.views.swiperefresh; +import androidx.annotation.Nullable; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; public class RefreshEvent extends Event { + @Deprecated protected RefreshEvent(int viewTag) { - super(viewTag); + this(-1, viewTag); + } + + protected RefreshEvent(int surfaceId, int viewTag) { + super(surfaceId, viewTag); } @Override @@ -21,8 +28,9 @@ public String getEventName() { return "topRefresh"; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), null); + protected WritableMap getEventData() { + return Arguments.createMap(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index c8462a138ccdc..888c7d8c0a308 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -88,11 +88,21 @@ public void setProgressBackgroundColor(ReactSwipeRefreshLayout view, Integer col } // TODO(T46143833): Remove this method once the 'size' prop has been migrated to String in JS. - @Override public void setSize(ReactSwipeRefreshLayout view, int value) { view.setSize(value); } + @Override + public void setSize(ReactSwipeRefreshLayout view, String size) { + if (size == null || size.equals("default")) { + view.setSize(SwipeRefreshLayout.DEFAULT); + } else if (size.equals("large")) { + view.setSize(SwipeRefreshLayout.LARGE); + } else { + throw new IllegalArgumentException("Size must be 'default' or 'large', received: " + size); + } + } + // This prop temporarily takes both 0 and 1 as well as 'default' and 'large'. // 0 and 1 are deprecated and will be removed in a future release. // See T46143833 @@ -103,15 +113,7 @@ public void setSize(ReactSwipeRefreshLayout view, Dynamic size) { } else if (size.getType() == ReadableType.Number) { view.setSize(size.asInt()); } else if (size.getType() == ReadableType.String) { - final String sizeStr = size.asString(); - if (sizeStr.equals("default")) { - view.setSize(SwipeRefreshLayout.DEFAULT); - } else if (sizeStr.equals("large")) { - view.setSize(SwipeRefreshLayout.LARGE); - } else { - throw new IllegalArgumentException( - "Size must be 'default' or 'large', received: " + sizeStr); - } + setSize(view, size.asString()); } else { throw new IllegalArgumentException("Size must be 'default' or 'large'"); } @@ -144,7 +146,8 @@ public void onRefresh() { EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.getId()); if (eventDispatcher != null) { - eventDispatcher.dispatchEvent(new RefreshEvent(view.getId())); + eventDispatcher.dispatchEvent( + new RefreshEvent(UIManagerHelper.getSurfaceId(view), view.getId())); } } }); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK index cd59cc2b72043..6042b28c9bb10 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK @@ -1,8 +1,9 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_target", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library") rn_android_library( name = "switchview", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ @@ -23,6 +24,6 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), + react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), ], ) diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitch.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitch.java index 1a2afde1eede3..4819bf2abd94e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitch.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitch.java @@ -8,8 +8,11 @@ package com.facebook.react.views.switchview; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; import androidx.annotation.Nullable; import androidx.appcompat.widget.SwitchCompat; @@ -59,6 +62,16 @@ public void setTrackColor(@Nullable Integer color) { public void setThumbColor(@Nullable Integer color) { setColor(super.getThumbDrawable(), color); + + // Set the ripple color with thumb color if >= LOLLIPOP + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + RippleDrawable ripple = (RippleDrawable) super.getBackground(); + ColorStateList customColorState = + new ColorStateList( + new int[][] {new int[] {android.R.attr.state_pressed}}, new int[] {color}); + + ripple.setColor(customColorState); + } } /*package*/ void setOn(boolean on) { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchEvent.java index 9cc34d0e43951..70f44a0354674 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.switchview; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by a ReactSwitchManager once a switch is fully switched on/off */ /*package*/ class ReactSwitchEvent extends Event { @@ -19,8 +19,13 @@ private final boolean mIsChecked; + @Deprecated public ReactSwitchEvent(int viewId, boolean isChecked) { - super(viewId); + this(-1, viewId, isChecked); + } + + public ReactSwitchEvent(int surfaceId, int viewId, boolean isChecked) { + super(surfaceId, viewId); mIsChecked = isChecked; } @@ -33,18 +38,9 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public short getCoalescingKey() { - // All switch events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putInt("target", getViewTag()); eventData.putBoolean("value", getIsChecked()); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java index c2196f3ff142d..40750ff9ced91 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java @@ -20,7 +20,7 @@ import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.ViewManagerDelegate; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.annotations.ReactProp; @@ -81,15 +81,11 @@ public long measure( public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { ReactContext reactContext = (ReactContext) buttonView.getContext(); - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); - - if (uiManager == null) { - return; - } - - uiManager - .getEventDispatcher() - .dispatchEvent(new ReactSwitchEvent(buttonView.getId(), isChecked)); + int reactTag = buttonView.getId(); + UIManagerHelper.getEventDispatcherForReactTag(reactContext, reactTag) + .dispatchEvent( + new ReactSwitchEvent( + UIManagerHelper.getSurfaceId(reactContext), reactTag, isChecked)); } }; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK index ac69e03c25835..cf4fbdd897016 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "text", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, @@ -22,6 +23,7 @@ rn_android_library( react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/common/mapbuffer:mapbuffer"), react_native_target("java/com/facebook/react/config:config"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager:uimanager"), diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java index cf0c8b141b007..61900f19f6f2f 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java @@ -12,9 +12,10 @@ import android.graphics.Typeface; import android.text.TextPaint; import android.text.style.MetricAffectingSpan; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.facebook.infer.annotation.Nullsafe; +@Nullsafe(Nullsafe.Mode.LOCAL) public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan { /** @@ -40,7 +41,7 @@ public CustomStyleSpan( int fontWeight, @Nullable String fontFeatureSettings, @Nullable String fontFamily, - @NonNull AssetManager assetManager) { + AssetManager assetManager) { mStyle = fontStyle; mWeight = fontWeight; mFeatureSettings = fontFeatureSettings; @@ -54,21 +55,18 @@ public void updateDrawState(TextPaint ds) { } @Override - public void updateMeasureState(@NonNull TextPaint paint) { + public void updateMeasureState(TextPaint paint) { apply(paint, mStyle, mWeight, mFeatureSettings, mFontFamily, mAssetManager); } - /** Returns {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. */ public int getStyle() { - return (mStyle == ReactTextShadowNode.UNSET ? 0 : mStyle); + return mStyle == ReactBaseTextShadowNode.UNSET ? Typeface.NORMAL : mStyle; } - /** Returns {@link Typeface#NORMAL} or {@link Typeface#BOLD}. */ public int getWeight() { - return (mWeight == ReactTextShadowNode.UNSET ? 0 : mWeight); + return mWeight == ReactBaseTextShadowNode.UNSET ? TypefaceStyle.NORMAL : mWeight; } - /** Returns the font family set for this StyleSpan. */ public @Nullable String getFontFamily() { return mFontFamily; } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java index 6835918b587d5..f685bf567b245 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java @@ -178,15 +178,12 @@ private static void buildSpannedFromShadowNode( new SetSpanOperation( start, end, new ReactBackgroundColorSpan(textShadowNode.mBackgroundColor))); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - float effectiveLetterSpacing = textAttributes.getEffectiveLetterSpacing(); - if (!Float.isNaN(effectiveLetterSpacing) - && (parentTextAttributes == null - || parentTextAttributes.getEffectiveLetterSpacing() != effectiveLetterSpacing)) { - ops.add( - new SetSpanOperation( - start, end, new CustomLetterSpacingSpan(effectiveLetterSpacing))); - } + float effectiveLetterSpacing = textAttributes.getEffectiveLetterSpacing(); + if (!Float.isNaN(effectiveLetterSpacing) + && (parentTextAttributes == null + || parentTextAttributes.getEffectiveLetterSpacing() != effectiveLetterSpacing)) { + ops.add( + new SetSpanOperation(start, end, new CustomLetterSpacingSpan(effectiveLetterSpacing))); } int effectiveFontSize = textAttributes.getEffectiveFontSize(); if ( // `getEffectiveFontSize` always returns a value so don't need to check for anything like diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactClickableSpan.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactClickableSpan.java index 37f7000a915b9..bb093d2691ddc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactClickableSpan.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactClickableSpan.java @@ -52,7 +52,8 @@ public void onClick(@NonNull View view) { EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, mReactTag); if (eventDispatcher != null) { - eventDispatcher.dispatchEvent(new ViewGroupClickEvent(mReactTag)); + eventDispatcher.dispatchEvent( + new ViewGroupClickEvent(UIManagerHelper.getSurfaceId(context), mReactTag)); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java index 32f0219db876d..077f6006887c3 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java @@ -10,31 +10,39 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; -import android.os.Build; import android.util.SparseArray; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; +import com.facebook.infer.annotation.Nullsafe; import java.util.HashMap; import java.util.Map; /** - * Class responsible to load and cache Typeface objects. It will first try to load typefaces inside - * the assets/fonts folder and if it doesn't find the right Typeface in that folder will fall back - * on the best matching system Typeface The supported custom fonts extensions are .ttf and .otf. For - * each font family the bold, italic and bold_italic variants are supported. Given a "family" font - * family the files in the assets/fonts folder need to be family.ttf(.otf) family_bold.ttf(.otf) - * family_italic.ttf(.otf) and family_bold_italic.ttf(.otf) + * Responsible for loading and caching Typeface objects. + * + *

This will first try to load a typeface from the assets/fonts folder. If one is not found in + * that folder, this will fallback to the best matching system typeface. + * + *

Custom fonts support the extensions `.ttf` and `.otf` and the variants `bold`, `italic`, and + * `bold_italic`. For example, given a font named "ExampleFontFamily", the following are supported: + * + *

    + *
  • ExampleFontFamily.ttf (or .otf) + *
  • ExampleFontFamily_bold.ttf (or .otf) + *
  • ExampleFontFamily_italic.ttf (or .otf) + *
  • ExampleFontFamily_bold_italic.ttf (or .otf) */ +@Nullsafe(Nullsafe.Mode.LOCAL) public class ReactFontManager { + // NOTE: Indices in `EXTENSIONS` correspond to the `TypeFace` style constants. private static final String[] EXTENSIONS = {"", "_bold", "_italic", "_bold_italic"}; private static final String[] FILE_EXTENSIONS = {".ttf", ".otf"}; private static final String FONTS_ASSET_PATH = "fonts/"; private static ReactFontManager sReactFontManagerInstance; - private final Map mFontCache; + private final Map mFontCache; private final Map mCustomTypefaceCache; private ReactFontManager() { @@ -49,36 +57,43 @@ public static ReactFontManager getInstance() { return sReactFontManagerInstance; } - public @Nullable Typeface getTypeface( - String fontFamilyName, int style, AssetManager assetManager) { - return getTypeface(fontFamilyName, style, 0, assetManager); + public Typeface getTypeface(String fontFamilyName, int style, AssetManager assetManager) { + return getTypeface(fontFamilyName, new TypefaceStyle(style), assetManager); } - public @Nullable Typeface getTypeface( + public Typeface getTypeface( + String fontFamilyName, int weight, boolean italic, AssetManager assetManager) { + return getTypeface(fontFamilyName, new TypefaceStyle(weight, italic), assetManager); + } + + public Typeface getTypeface( String fontFamilyName, int style, int weight, AssetManager assetManager) { + return getTypeface(fontFamilyName, new TypefaceStyle(style, weight), assetManager); + } + + public Typeface getTypeface( + String fontFamilyName, TypefaceStyle typefaceStyle, AssetManager assetManager) { if (mCustomTypefaceCache.containsKey(fontFamilyName)) { - Typeface typeface = mCustomTypefaceCache.get(fontFamilyName); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && weight >= 100 && weight <= 1000) { - return Typeface.create(typeface, weight, (style & Typeface.ITALIC) != 0); - } - return Typeface.create(typeface, style); + // Apply `typefaceStyle` because custom fonts configure variants using `app:fontStyle` and + // `app:fontWeight` in their resource XML configuration file. + return typefaceStyle.apply(mCustomTypefaceCache.get(fontFamilyName)); } - FontFamily fontFamily = mFontCache.get(fontFamilyName); - if (fontFamily == null) { - fontFamily = new FontFamily(); - mFontCache.put(fontFamilyName, fontFamily); + AssetFontFamily assetFontFamily = mFontCache.get(fontFamilyName); + if (assetFontFamily == null) { + assetFontFamily = new AssetFontFamily(); + mFontCache.put(fontFamilyName, assetFontFamily); } - Typeface typeface = fontFamily.getTypeface(style); - if (typeface == null) { - typeface = createTypeface(fontFamilyName, style, assetManager); - if (typeface != null) { - fontFamily.setTypeface(style, typeface); - } - } + int style = typefaceStyle.getNearestStyle(); - return typeface; + Typeface assetTypeface = assetFontFamily.getTypefaceForStyle(style); + if (assetTypeface == null) { + assetTypeface = createAssetTypeface(fontFamilyName, style, assetManager); + assetFontFamily.setTypefaceForStyle(style, assetTypeface); + } + // Do not apply `typefaceStyle` because asset font files already incorporate the style. + return assetTypeface; } /* @@ -88,7 +103,7 @@ public static ReactFontManager getInstance() { * * ReactFontManager.getInstance().addCustomFont(this, "Srisakdi", R.font.srisakdi); */ - public void addCustomFont(@NonNull Context context, @NonNull String fontFamily, int fontId) { + public void addCustomFont(Context context, String fontFamily, int fontId) { Typeface font = ResourcesCompat.getFont(context, fontId); if (font != null) { mCustomTypefaceCache.put(fontFamily, font); @@ -106,16 +121,16 @@ public void addCustomFont(@NonNull Context context, @NonNull String fontFamily, */ public void setTypeface(String fontFamilyName, int style, Typeface typeface) { if (typeface != null) { - FontFamily fontFamily = mFontCache.get(fontFamilyName); - if (fontFamily == null) { - fontFamily = new FontFamily(); - mFontCache.put(fontFamilyName, fontFamily); + AssetFontFamily assetFontFamily = mFontCache.get(fontFamilyName); + if (assetFontFamily == null) { + assetFontFamily = new AssetFontFamily(); + mFontCache.put(fontFamilyName, assetFontFamily); } - fontFamily.setTypeface(style, typeface); + assetFontFamily.setTypefaceForStyle(style, typeface); } } - private static @Nullable Typeface createTypeface( + private static Typeface createAssetTypeface( String fontFamilyName, int style, AssetManager assetManager) { String extension = EXTENSIONS[style]; for (String fileExtension : FILE_EXTENSIONS) { @@ -129,27 +144,27 @@ public void setTypeface(String fontFamilyName, int style, Typeface typeface) { try { return Typeface.createFromAsset(assetManager, fileName); } catch (RuntimeException e) { - // unfortunately Typeface.createFromAsset throws an exception instead of returning null - // if the typeface doesn't exist + // If the typeface asset does not exist, try another extension. + continue; } } - return Typeface.create(fontFamilyName, style); } - private static class FontFamily { + /** Responsible for caching typefaces for each custom font family. */ + private static class AssetFontFamily { private SparseArray mTypefaceSparseArray; - private FontFamily() { + private AssetFontFamily() { mTypefaceSparseArray = new SparseArray<>(4); } - public Typeface getTypeface(int style) { + public @Nullable Typeface getTypefaceForStyle(int style) { return mTypefaceSparseArray.get(style); } - public void setTypeface(int style, Typeface typeface) { + public void setTypefaceForStyle(int style, Typeface typeface) { mTypefaceSparseArray.put(style, typeface); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java index ffc8655c0a47c..6ee067cb661a5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java @@ -110,10 +110,6 @@ public void setAndroidHyphenationFrequency(ReactTextView view, @Nullable String view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE); } else if (frequency.equals("full")) { view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); - } else if (frequency.equals("balanced")) { - view.setHyphenationFrequency(Layout.BREAK_STRATEGY_BALANCED); - } else if (frequency.equals("high")) { - view.setHyphenationFrequency(Layout.BREAK_STRATEGY_HIGH_QUALITY); } else if (frequency.equals("normal")) { view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); } else { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index 5dbd0796e5336..26257ebe93881 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -21,7 +21,7 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactNoCrashSoftException; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.NativeViewHierarchyOptimizer; @@ -32,6 +32,7 @@ import com.facebook.react.uimanager.UIViewOperationQueue; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.facebook.yoga.YogaBaselineFunction; import com.facebook.yoga.YogaConstants; import com.facebook.yoga.YogaDirection; import com.facebook.yoga.YogaMeasureFunction; @@ -111,12 +112,12 @@ public long measure( text, layout, sTextPaintInstance, themedReactContext); WritableMap event = Arguments.createMap(); event.putArray("lines", lines); - if (themedReactContext.hasActiveCatalystInstance()) { + if (themedReactContext.hasActiveReactInstance()) { themedReactContext .getJSModule(RCTEventEmitter.class) .receiveEvent(getReactTag(), "topTextLayout", event); } else { - ReactSoftException.logSoftException( + ReactSoftExceptionLogger.logSoftException( "ReactTextShadowNode", new ReactNoCrashSoftException("Cannot get RCTEventEmitter, no CatalystInstance")); } @@ -159,6 +160,20 @@ public long measure( } }; + private final YogaBaselineFunction mTextBaselineFunction = + new YogaBaselineFunction() { + @Override + public float baseline(YogaNode node, float width, float height) { + Spannable text = + Assertions.assertNotNull( + mPreparedSpannableText, + "Spannable element has not been prepared in onBeforeLayout"); + + Layout layout = measureSpannedText(text, width, YogaMeasureMode.EXACTLY); + return layout.getLineBaseline(layout.getLineCount() - 1); + } + }; + public ReactTextShadowNode() { this(null); } @@ -171,6 +186,7 @@ public ReactTextShadowNode(@Nullable ReactTextViewManagerCallback reactTextViewM private void initMeasureFunction() { if (!isVirtual()) { setMeasureFunction(mTextMeasureFunction); + setBaselineFunction(mTextBaselineFunction); } } @@ -231,7 +247,14 @@ private Layout measureSpannedText(Spannable text, float width, YogaMeasureMode w // than the width of the text. layout = BoringLayout.make( - text, textPaint, boring.width, alignment, 1.f, 0.f, boring, mIncludeFontPadding); + text, + textPaint, + Math.max(boring.width, 0), + alignment, + 1.f, + 0.f, + boring, + mIncludeFontPadding); } else { // Is used for multiline, boring text and the width is known. @@ -320,6 +343,11 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) { mJustificationMode); uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate); } + + if (mAdjustsFontSizeToFit) { + // Nodes with `adjustsFontSizeToFit` enabled need to be remeasured on every relayout. + markUpdated(); + } } @ReactProp(name = "onTextLayout") diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java index f7a0a18995fe7..a6d48f57d17aa 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java @@ -56,6 +56,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie private boolean mAdjustsFontSizeToFit = false; private int mLinkifyMaskType = 0; private boolean mNotifyOnInlineViewLayout; + private boolean mTextIsSelectable = false; private ReactViewBackgroundManager mReactBackgroundManager; private Spannable mSpanned; @@ -124,6 +125,17 @@ protected void onLayout( Spanned text = (Spanned) getText(); Layout layout = getLayout(); + if (layout == null) { + // Text layout is calculated during pre-draw phase, so in some cases it can be empty during + // layout phase, which usually happens before drawing. + // The text layout is created by private {@link assumeLayout} method, which we can try to + // invoke directly through reflection or indirectly through some methods that compute it + // (e.g. {@link getExtendedPaddingTop}). + // It is safer, however, to just early return here, as next measure/layout passes are way more + // likely to have the text layout computed. + return; + } + TextInlineViewPlaceholderSpan[] placeholders = text.getSpans(0, text.length(), TextInlineViewPlaceholderSpan.class); ArrayList inlineViewInfoArray = @@ -359,7 +371,7 @@ public int reactTagForTouch(float touchX, float touchY) { for (int i = 0; i < spans.length; i++) { int spanStart = spannedText.getSpanStart(spans[i]); int spanEnd = spannedText.getSpanEnd(spans[i]); - if (spanEnd > index && (spanEnd - spanStart) <= targetSpanTextLength) { + if (spanEnd >= index && (spanEnd - spanStart) <= targetSpanTextLength) { target = spans[i].getReactTag(); targetSpanTextLength = (spanEnd - spanStart); } @@ -422,9 +434,16 @@ public void onStartTemporaryDetach() { } } + @Override + public void setTextIsSelectable(boolean selectable) { + mTextIsSelectable = selectable; + super.setTextIsSelectable(selectable); + } + @Override public void onAttachedToWindow() { super.onAttachedToWindow(); + setTextIsSelectable(mTextIsSelectable); if (mContainsImages && getText() instanceof Spanned) { Spanned text = (Spanned) getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index 7984406e0e7d8..f4f618c5ef95e 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -14,6 +14,8 @@ import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.annotations.VisibleForTesting; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.IViewManagerWithChildren; import com.facebook.react.uimanager.ReactStylesDiffMap; @@ -31,6 +33,12 @@ public class ReactTextViewManager extends ReactTextAnchorViewManager implements IViewManagerWithChildren { + private static final short TX_STATE_KEY_ATTRIBUTED_STRING = 0; + private static final short TX_STATE_KEY_PARAGRAPH_ATTRIBUTES = 1; + // used for text input + private static final short TX_STATE_KEY_HASH = 2; + private static final short TX_STATE_KEY_MOST_RECENT_EVENT_COUNT = 3; + @VisibleForTesting public static final String REACT_CLASS = "RCTText"; protected @Nullable ReactTextViewManagerCallback mReactTextViewManagerCallback; @@ -83,7 +91,22 @@ public boolean needsCustomLayoutForChildren() { @Override public Object updateState( ReactTextView view, ReactStylesDiffMap props, @Nullable StateWrapper stateWrapper) { - ReadableNativeMap state = stateWrapper.getState(); + if (stateWrapper == null) { + return null; + } + + if (ReactFeatureFlags.isMapBufferSerializationEnabled()) { + ReadableMapBuffer stateMapBuffer = stateWrapper.getStatDataMapBuffer(); + if (stateMapBuffer != null) { + return getReactTextUpdate(view, props, stateMapBuffer); + } + } + + ReadableNativeMap state = stateWrapper.getStateData(); + if (state == null) { + return null; + } + ReadableMap attributedString = state.getMap("attributedString"); ReadableMap paragraphAttributes = state.getMap("paragraphAttributes"); Spannable spanned = @@ -103,6 +126,30 @@ public Object updateState( TextAttributeProps.getJustificationMode(props)); } + private Object getReactTextUpdate( + ReactTextView view, ReactStylesDiffMap props, ReadableMapBuffer state) { + + ReadableMapBuffer attributedString = state.getMapBuffer(TX_STATE_KEY_ATTRIBUTED_STRING); + ReadableMapBuffer paragraphAttributes = state.getMapBuffer(TX_STATE_KEY_PARAGRAPH_ATTRIBUTES); + Spannable spanned = + TextLayoutManagerMapBuffer.getOrCreateSpannableForText( + view.getContext(), attributedString, mReactTextViewManagerCallback); + view.setSpanned(spanned); + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_TEXT_BREAK_STRATEGY)); + + return new ReactTextUpdate( + spanned, + -1, // UNUSED FOR TEXT + false, // TODO add this into local Data + TextAttributeProps.getTextAlignment( + props, TextLayoutManagerMapBuffer.isRTL(attributedString)), + textBreakStrategy, + TextAttributeProps.getJustificationMode(props)); + } + @Override public @Nullable Map getExportedCustomDirectEventTypeConstants() { return MapBuilder.of( diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java index 237fc3ff7bb5c..85948ef78e898 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java @@ -11,33 +11,52 @@ import android.graphics.Typeface; import android.text.TextUtils; import androidx.annotation.Nullable; +import com.facebook.infer.annotation.Nullsafe; import com.facebook.react.bridge.ReadableArray; import java.util.ArrayList; import java.util.List; +@Nullsafe(Nullsafe.Mode.LOCAL) public class ReactTypefaceUtils { - public static final int UNSET = -1; public static int parseFontWeight(@Nullable String fontWeightString) { - int fontWeightNumeric = - fontWeightString != null ? parseNumericFontWeight(fontWeightString) : UNSET; - int fontWeight = fontWeightNumeric != UNSET ? fontWeightNumeric : Typeface.NORMAL; - - if (fontWeight == 700 || "bold".equals(fontWeightString)) fontWeight = Typeface.BOLD; - else if (fontWeight == 400 || "normal".equals(fontWeightString)) fontWeight = Typeface.NORMAL; - - return fontWeight; + if (fontWeightString != null) { + switch (fontWeightString) { + case "100": + return 100; + case "200": + return 200; + case "300": + return 300; + case "normal": + case "400": + return 400; + case "500": + return 500; + case "600": + return 600; + case "bold": + case "700": + return 700; + case "800": + return 800; + case "900": + return 900; + } + } + return ReactBaseTextShadowNode.UNSET; } public static int parseFontStyle(@Nullable String fontStyleString) { - int fontStyle = UNSET; - if ("italic".equals(fontStyleString)) { - fontStyle = Typeface.ITALIC; - } else if ("normal".equals(fontStyleString)) { - fontStyle = Typeface.NORMAL; + if (fontStyleString != null) { + if ("italic".equals(fontStyleString)) { + return Typeface.ITALIC; + } + if ("normal".equals(fontStyleString)) { + return Typeface.NORMAL; + } } - - return fontStyle; + return ReactBaseTextShadowNode.UNSET; } public static @Nullable String parseFontVariant(@Nullable ReadableArray fontVariantArray) { @@ -77,51 +96,14 @@ public static Typeface applyStyles( @Nullable Typeface typeface, int style, int weight, - @Nullable String family, + @Nullable String fontFamilyName, AssetManager assetManager) { - int oldStyle; - if (typeface == null) { - oldStyle = 0; - } else { - oldStyle = typeface.getStyle(); - } - - int want = 0; - if ((weight == Typeface.BOLD) - || ((oldStyle & Typeface.BOLD) != 0 && weight == ReactTextShadowNode.UNSET)) { - want |= Typeface.BOLD; - } - - if ((style == Typeface.ITALIC) - || ((oldStyle & Typeface.ITALIC) != 0 && style == ReactTextShadowNode.UNSET)) { - want |= Typeface.ITALIC; - } - - if (family != null) { - typeface = ReactFontManager.getInstance().getTypeface(family, want, weight, assetManager); - } else if (typeface != null) { - // TODO(t9055065): Fix custom fonts getting applied to text children with different style - typeface = Typeface.create(typeface, want); - } - - if (typeface != null) { - return typeface; + TypefaceStyle typefaceStyle = new TypefaceStyle(style, weight); + if (fontFamilyName == null) { + return typefaceStyle.apply(typeface == null ? Typeface.DEFAULT : typeface); } else { - return Typeface.defaultFromStyle(want); + return ReactFontManager.getInstance() + .getTypeface(fontFamilyName, typefaceStyle, assetManager); } } - - /** - * Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise - * return the weight. - */ - private static int parseNumericFontWeight(String fontWeightString) { - // This should be much faster than using regex to verify input and Integer.parseInt - return fontWeightString.length() == 3 - && fontWeightString.endsWith("00") - && fontWeightString.charAt(0) <= '9' - && fontWeightString.charAt(0) >= '1' - ? 100 * (fontWeightString.charAt(0) - '0') - : UNSET; - } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java index f99e24019e523..6c93d200e8579 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java @@ -7,27 +7,54 @@ package com.facebook.react.views.text; -import android.graphics.Typeface; import android.os.Build; import android.text.Layout; +import android.text.TextUtils; import android.util.LayoutDirection; import android.view.Gravity; import androidx.annotation.Nullable; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactAccessibilityDelegate; import com.facebook.react.uimanager.ReactStylesDiffMap; import com.facebook.react.uimanager.ViewProps; -import com.facebook.yoga.YogaDirection; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; // TODO: T63643819 refactor naming of TextAttributeProps to make explicit that this represents // TextAttributes and not TextProps. As part of this refactor extract methods that don't belong to // TextAttributeProps (e.g. TextAlign) public class TextAttributeProps { - private static final String INLINE_IMAGE_PLACEHOLDER = "I"; + // constants for Text Attributes serialization + public static final short TA_KEY_FOREGROUND_COLOR = 0; + public static final short TA_KEY_BACKGROUND_COLOR = 1; + public static final short TA_KEY_OPACITY = 2; + public static final short TA_KEY_FONT_FAMILY = 3; + public static final short TA_KEY_FONT_SIZE = 4; + public static final short TA_KEY_FONT_SIZE_MULTIPLIER = 5; + public static final short TA_KEY_FONT_WEIGHT = 6; + public static final short TA_KEY_FONT_STYLE = 7; + public static final short TA_KEY_FONT_VARIANT = 8; + public static final short TA_KEY_ALLOW_FONT_SCALING = 9; + public static final short TA_KEY_LETTER_SPACING = 10; + public static final short TA_KEY_LINE_HEIGHT = 11; + public static final short TA_KEY_ALIGNMENT = 12; + public static final short TA_KEY_BEST_WRITING_DIRECTION = 13; + public static final short TA_KEY_TEXT_DECORATION_COLOR = 14; + public static final short TA_KEY_TEXT_DECORATION_LINE = 15; + public static final short TA_KEY_TEXT_DECORATION_LINE_STYLE = 16; + public static final short TA_KEY_TEXT_DECORATION_LINE_PATTERN = 17; + public static final short TA_KEY_TEXT_SHADOW_RAIDUS = 18; + public static final short TA_KEY_TEXT_SHADOW_COLOR = 19; + public static final short TA_KEY_IS_HIGHLIGHTED = 20; + public static final short TA_KEY_LAYOUT_DIRECTION = 21; + public static final short TA_KEY_ACCESSIBILITY_ROLE = 22; + public static final int UNSET = -1; private static final String PROP_SHADOW_OFFSET = "textShadowOffset"; @@ -41,9 +68,10 @@ public class TextAttributeProps { private static final int DEFAULT_TEXT_SHADOW_COLOR = 0x55000000; private static final int DEFAULT_JUSTIFICATION_MODE = (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) ? 0 : Layout.JUSTIFICATION_MODE_NONE; - private static final int DEFAULT_BREAK_STRATEGY = (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ? 0 : Layout.BREAK_STRATEGY_HIGH_QUALITY; + private static final int DEFAULT_HYPHENATION_FREQUENCY = + (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ? 0 : Layout.HYPHENATION_FREQUENCY_NONE; protected float mLineHeight = Float.NaN; protected boolean mIsColorSet = false; @@ -62,7 +90,7 @@ public class TextAttributeProps { // `UNSET` is -1 and is the same as `LayoutDirection.UNDEFINED` but the symbol isn't available. protected int mLayoutDirection = UNSET; - protected TextTransform mTextTransform = TextTransform.UNSET; + protected TextTransform mTextTransform = TextTransform.NONE; protected float mTextShadowOffsetDx = 0; protected float mTextShadowOffsetDy = 0; @@ -76,12 +104,7 @@ public class TextAttributeProps { protected @Nullable ReactAccessibilityDelegate.AccessibilityRole mAccessibilityRole = null; protected boolean mIsAccessibilityRoleSet = false; - /** - * mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. mFontWeight can be {@link - * Typeface#NORMAL} or {@link Typeface#BOLD}. - */ protected int mFontStyle = UNSET; - protected int mFontWeight = UNSET; /** * NB: If a font family is used that does not have a style in a certain Android version (ie. @@ -112,33 +135,123 @@ public class TextAttributeProps { protected boolean mContainsImages = false; protected float mHeightOfTallestInlineImage = Float.NaN; - private final ReactStylesDiffMap mProps; - - public TextAttributeProps(ReactStylesDiffMap props) { - mProps = props; - setNumberOfLines(getIntProp(ViewProps.NUMBER_OF_LINES, UNSET)); - setLineHeight(getFloatProp(ViewProps.LINE_HEIGHT, UNSET)); - setLetterSpacing(getFloatProp(ViewProps.LETTER_SPACING, Float.NaN)); - setAllowFontScaling(getBooleanProp(ViewProps.ALLOW_FONT_SCALING, true)); - setFontSize(getFloatProp(ViewProps.FONT_SIZE, UNSET)); - setColor(props.hasKey(ViewProps.COLOR) ? props.getInt(ViewProps.COLOR, 0) : null); - setColor(props.hasKey("foregroundColor") ? props.getInt("foregroundColor", 0) : null); - setBackgroundColor( + private TextAttributeProps() {} + + /** + * Build a TextAttributeProps using data from the {@link ReadableMapBuffer} received as a + * parameter. + */ + public static TextAttributeProps fromReadableMapBuffer(ReadableMapBuffer props) { + TextAttributeProps result = new TextAttributeProps(); + + // TODO T83483191: Review constants that are not being set! + Iterator iterator = props.iterator(); + while (iterator.hasNext()) { + ReadableMapBuffer.MapBufferEntry entry = iterator.next(); + switch (entry.getKey()) { + case TA_KEY_FOREGROUND_COLOR: + result.setColor(entry.getInt(0)); + break; + case TA_KEY_BACKGROUND_COLOR: + result.setBackgroundColor(entry.getInt(0)); + break; + case TA_KEY_OPACITY: + break; + case TA_KEY_FONT_FAMILY: + result.setFontFamily(entry.getString()); + break; + case TA_KEY_FONT_SIZE: + result.setFontSize((float) entry.getDouble(UNSET)); + break; + case TA_KEY_FONT_SIZE_MULTIPLIER: + break; + case TA_KEY_FONT_WEIGHT: + result.setFontWeight(entry.getString()); + break; + case TA_KEY_FONT_STYLE: + result.setFontStyle(entry.getString()); + break; + case TA_KEY_FONT_VARIANT: + result.setFontVariant(entry.getReadableMapBuffer()); + break; + case TA_KEY_ALLOW_FONT_SCALING: + result.setAllowFontScaling(entry.getBoolean(true)); + break; + case TA_KEY_LETTER_SPACING: + result.setLetterSpacing((float) entry.getDouble(Float.NaN)); + break; + case TA_KEY_LINE_HEIGHT: + result.setLineHeight((float) entry.getDouble(UNSET)); + break; + case TA_KEY_ALIGNMENT: + break; + case TA_KEY_BEST_WRITING_DIRECTION: + break; + case TA_KEY_TEXT_DECORATION_COLOR: + break; + case TA_KEY_TEXT_DECORATION_LINE: + result.setTextDecorationLine(entry.getString()); + break; + case TA_KEY_TEXT_DECORATION_LINE_STYLE: + break; + case TA_KEY_TEXT_DECORATION_LINE_PATTERN: + break; + case TA_KEY_TEXT_SHADOW_RAIDUS: + result.setTextShadowRadius(entry.getInt(1)); + break; + case TA_KEY_TEXT_SHADOW_COLOR: + result.setTextShadowColor(entry.getInt(DEFAULT_TEXT_SHADOW_COLOR)); + break; + case TA_KEY_IS_HIGHLIGHTED: + break; + case TA_KEY_LAYOUT_DIRECTION: + result.setLayoutDirection(entry.getString()); + break; + case TA_KEY_ACCESSIBILITY_ROLE: + result.setAccessibilityRole(entry.getString()); + break; + } + } + + // TODO T83483191: Review why the following props are not serialized: + // setNumberOfLines + // setColor + // setIncludeFontPadding + // setTextShadowOffset + // setTextTransform + return result; + } + + public static TextAttributeProps fromReadableMap(ReactStylesDiffMap props) { + TextAttributeProps result = new TextAttributeProps(); + result.setNumberOfLines(getIntProp(props, ViewProps.NUMBER_OF_LINES, UNSET)); + result.setLineHeight(getFloatProp(props, ViewProps.LINE_HEIGHT, UNSET)); + result.setLetterSpacing(getFloatProp(props, ViewProps.LETTER_SPACING, Float.NaN)); + result.setAllowFontScaling(getBooleanProp(props, ViewProps.ALLOW_FONT_SCALING, true)); + result.setFontSize(getFloatProp(props, ViewProps.FONT_SIZE, UNSET)); + result.setColor(props.hasKey(ViewProps.COLOR) ? props.getInt(ViewProps.COLOR, 0) : null); + result.setColor( + props.hasKey(ViewProps.FOREGROUND_COLOR) + ? props.getInt(ViewProps.FOREGROUND_COLOR, 0) + : null); + result.setBackgroundColor( props.hasKey(ViewProps.BACKGROUND_COLOR) ? props.getInt(ViewProps.BACKGROUND_COLOR, 0) : null); - setFontFamily(getStringProp(ViewProps.FONT_FAMILY)); - setFontWeight(getStringProp(ViewProps.FONT_WEIGHT)); - setFontStyle(getStringProp(ViewProps.FONT_STYLE)); - setFontVariant(getArrayProp(ViewProps.FONT_VARIANT)); - setIncludeFontPadding(getBooleanProp(ViewProps.INCLUDE_FONT_PADDING, true)); - setTextDecorationLine(getStringProp(ViewProps.TEXT_DECORATION_LINE)); - setTextShadowOffset(props.hasKey(PROP_SHADOW_OFFSET) ? props.getMap(PROP_SHADOW_OFFSET) : null); - setTextShadowRadius(getIntProp(PROP_SHADOW_RADIUS, 1)); - setTextShadowColor(getIntProp(PROP_SHADOW_COLOR, DEFAULT_TEXT_SHADOW_COLOR)); - setTextTransform(getStringProp(PROP_TEXT_TRANSFORM)); - setLayoutDirection(getStringProp(ViewProps.LAYOUT_DIRECTION)); - setAccessibilityRole(getStringProp(ViewProps.ACCESSIBILITY_ROLE)); + result.setFontFamily(getStringProp(props, ViewProps.FONT_FAMILY)); + result.setFontWeight(getStringProp(props, ViewProps.FONT_WEIGHT)); + result.setFontStyle(getStringProp(props, ViewProps.FONT_STYLE)); + result.setFontVariant(getArrayProp(props, ViewProps.FONT_VARIANT)); + result.setIncludeFontPadding(getBooleanProp(props, ViewProps.INCLUDE_FONT_PADDING, true)); + result.setTextDecorationLine(getStringProp(props, ViewProps.TEXT_DECORATION_LINE)); + result.setTextShadowOffset( + props.hasKey(PROP_SHADOW_OFFSET) ? props.getMap(PROP_SHADOW_OFFSET) : null); + result.setTextShadowRadius(getIntProp(props, PROP_SHADOW_RADIUS, 1)); + result.setTextShadowColor(getIntProp(props, PROP_SHADOW_COLOR, DEFAULT_TEXT_SHADOW_COLOR)); + result.setTextTransform(getStringProp(props, PROP_TEXT_TRANSFORM)); + result.setLayoutDirection(getStringProp(props, ViewProps.LAYOUT_DIRECTION)); + result.setAccessibilityRole(getStringProp(props, ViewProps.ACCESSIBILITY_ROLE)); + return result; } public static int getTextAlignment(ReactStylesDiffMap props, boolean isRTL) { @@ -176,7 +289,8 @@ public static int getJustificationMode(ReactStylesDiffMap props) { return DEFAULT_JUSTIFICATION_MODE; } - private boolean getBooleanProp(String name, boolean defaultValue) { + private static boolean getBooleanProp( + ReactStylesDiffMap mProps, String name, boolean defaultValue) { if (mProps.hasKey(name)) { return mProps.getBoolean(name, defaultValue); } else { @@ -184,7 +298,7 @@ private boolean getBooleanProp(String name, boolean defaultValue) { } } - private String getStringProp(String name) { + private static String getStringProp(ReactStylesDiffMap mProps, String name) { if (mProps.hasKey(name)) { return mProps.getString(name); } else { @@ -192,7 +306,7 @@ private String getStringProp(String name) { } } - private int getIntProp(String name, int defaultvalue) { + private static int getIntProp(ReactStylesDiffMap mProps, String name, int defaultvalue) { if (mProps.hasKey(name)) { return mProps.getInt(name, defaultvalue); } else { @@ -200,7 +314,7 @@ private int getIntProp(String name, int defaultvalue) { } } - private float getFloatProp(String name, float defaultvalue) { + private static float getFloatProp(ReactStylesDiffMap mProps, String name, float defaultvalue) { if (mProps.hasKey(name)) { return mProps.getFloat(name, defaultvalue); } else { @@ -208,7 +322,7 @@ private float getFloatProp(String name, float defaultvalue) { } } - private @Nullable ReadableArray getArrayProp(String name) { + private static @Nullable ReadableArray getArrayProp(ReactStylesDiffMap mProps, String name) { if (mProps.hasKey(name)) { return mProps.getArray(name); } else { @@ -226,11 +340,11 @@ public float getEffectiveLineHeight() { return useInlineViewHeight ? mHeightOfTallestInlineImage : mLineHeight; } - public void setNumberOfLines(int numberOfLines) { + private void setNumberOfLines(int numberOfLines) { mNumberOfLines = numberOfLines == 0 ? UNSET : numberOfLines; } - public void setLineHeight(float lineHeight) { + private void setLineHeight(float lineHeight) { mLineHeightInput = lineHeight; if (lineHeight == UNSET) { mLineHeight = Float.NaN; @@ -242,7 +356,7 @@ public void setLineHeight(float lineHeight) { } } - public void setLetterSpacing(float letterSpacing) { + private void setLetterSpacing(float letterSpacing) { mLetterSpacingInput = letterSpacing; } @@ -261,7 +375,7 @@ public float getLetterSpacing() { return letterSpacingPixels / mFontSize; } - public void setAllowFontScaling(boolean allowFontScaling) { + private void setAllowFontScaling(boolean allowFontScaling) { if (allowFontScaling != mAllowFontScaling) { mAllowFontScaling = allowFontScaling; setFontSize(mFontSizeInput); @@ -270,7 +384,7 @@ public void setAllowFontScaling(boolean allowFontScaling) { } } - public void setFontSize(float fontSize) { + private void setFontSize(float fontSize) { mFontSizeInput = fontSize; if (fontSize != UNSET) { fontSize = @@ -281,14 +395,14 @@ public void setFontSize(float fontSize) { mFontSize = (int) fontSize; } - public void setColor(@Nullable Integer color) { + private void setColor(@Nullable Integer color) { mIsColorSet = (color != null); if (mIsColorSet) { mColor = color; } } - public void setBackgroundColor(Integer color) { + private void setBackgroundColor(Integer color) { // TODO: Don't apply background color to anchor TextView since it will be applied on the View // directly // if (!isVirtualAnchor()) { @@ -299,54 +413,61 @@ public void setBackgroundColor(Integer color) { // } } - public void setFontFamily(@Nullable String fontFamily) { + private void setFontFamily(@Nullable String fontFamily) { mFontFamily = fontFamily; } - public void setFontVariant(@Nullable ReadableArray fontVariant) { + private void setFontVariant(@Nullable ReadableArray fontVariant) { mFontFeatureSettings = ReactTypefaceUtils.parseFontVariant(fontVariant); } - /** - * /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they - * can both use - */ - public void setFontWeight(@Nullable String fontWeightString) { - int fontWeightNumeric = - fontWeightString != null ? parseNumericFontWeight(fontWeightString) : -1; - int fontWeight = UNSET; - if (fontWeightNumeric >= 500 || "bold".equals(fontWeightString)) { - fontWeight = Typeface.BOLD; - } else if ("normal".equals(fontWeightString) - || (fontWeightNumeric != -1 && fontWeightNumeric < 500)) { - fontWeight = Typeface.NORMAL; + private void setFontVariant(@Nullable ReadableMapBuffer fontVariant) { + if (fontVariant == null || fontVariant.getCount() == 0) { + mFontFeatureSettings = null; + return; } - if (fontWeight != mFontWeight) { - mFontWeight = fontWeight; + + List features = new ArrayList<>(); + Iterator iterator = fontVariant.iterator(); + while (iterator.hasNext()) { + ReadableMapBuffer.MapBufferEntry entry = iterator.next(); + String value = entry.getString(); + if (value != null) { + switch (value) { + case "small-caps": + features.add("'smcp'"); + break; + case "oldstyle-nums": + features.add("'onum'"); + break; + case "lining-nums": + features.add("'lnum'"); + break; + case "tabular-nums": + features.add("'tnum'"); + break; + case "proportional-nums": + features.add("'pnum'"); + break; + } + } } + mFontFeatureSettings = TextUtils.join(", ", features); } - /** - * /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they - * can both use - */ - public void setFontStyle(@Nullable String fontStyleString) { - int fontStyle = UNSET; - if ("italic".equals(fontStyleString)) { - fontStyle = Typeface.ITALIC; - } else if ("normal".equals(fontStyleString)) { - fontStyle = Typeface.NORMAL; - } - if (fontStyle != mFontStyle) { - mFontStyle = fontStyle; - } + private void setFontWeight(@Nullable String fontWeightString) { + mFontWeight = ReactTypefaceUtils.parseFontWeight(fontWeightString); + } + + private void setFontStyle(@Nullable String fontStyleString) { + mFontStyle = ReactTypefaceUtils.parseFontStyle(fontStyleString); } - public void setIncludeFontPadding(boolean includepad) { + private void setIncludeFontPadding(boolean includepad) { mIncludeFontPadding = includepad; } - public void setTextDecorationLine(@Nullable String textDecorationLineString) { + private void setTextDecorationLine(@Nullable String textDecorationLineString) { mIsUnderlineTextDecorationSet = false; mIsLineThroughTextDecorationSet = false; if (textDecorationLineString != null) { @@ -360,7 +481,7 @@ public void setTextDecorationLine(@Nullable String textDecorationLineString) { } } - public void setTextShadowOffset(ReadableMap offsetMap) { + private void setTextShadowOffset(ReadableMap offsetMap) { mTextShadowOffsetDx = 0; mTextShadowOffsetDy = 0; @@ -378,32 +499,38 @@ public void setTextShadowOffset(ReadableMap offsetMap) { } } - public void setLayoutDirection(@Nullable String layoutDirection) { + public static int getLayoutDirection(@Nullable String layoutDirection) { + int androidLayoutDirection; if (layoutDirection == null || "undefined".equals(layoutDirection)) { - mLayoutDirection = UNSET; + androidLayoutDirection = UNSET; } else if ("rtl".equals(layoutDirection)) { - mLayoutDirection = LayoutDirection.RTL; + androidLayoutDirection = LayoutDirection.RTL; } else if ("ltr".equals(layoutDirection)) { - mLayoutDirection = LayoutDirection.LTR; + androidLayoutDirection = LayoutDirection.LTR; } else { throw new JSApplicationIllegalArgumentException( "Invalid layoutDirection: " + layoutDirection); } + return androidLayoutDirection; } - public void setTextShadowRadius(float textShadowRadius) { + private void setLayoutDirection(@Nullable String layoutDirection) { + mLayoutDirection = getLayoutDirection(layoutDirection); + } + + private void setTextShadowRadius(float textShadowRadius) { if (textShadowRadius != mTextShadowRadius) { mTextShadowRadius = textShadowRadius; } } - public void setTextShadowColor(int textShadowColor) { + private void setTextShadowColor(int textShadowColor) { if (textShadowColor != mTextShadowColor) { mTextShadowColor = textShadowColor; } } - public void setTextTransform(@Nullable String textTransform) { + private void setTextTransform(@Nullable String textTransform) { if (textTransform == null || "none".equals(textTransform)) { mTextTransform = TextTransform.NONE; } else if ("uppercase".equals(textTransform)) { @@ -417,7 +544,7 @@ public void setTextTransform(@Nullable String textTransform) { } } - public void setAccessibilityRole(@Nullable String accessibilityRole) { + private void setAccessibilityRole(@Nullable String accessibilityRole) { if (accessibilityRole != null) { mIsAccessibilityRoleSet = accessibilityRole != null; mAccessibilityRole = @@ -443,57 +570,21 @@ public static int getTextBreakStrategy(@Nullable String textBreakStrategy) { return androidTextBreakStrategy; } - /** - * Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise - * return the weight. - * - *

    This code is duplicated in ReactTextInputManager TODO: Factor into a common place they can - * both use - */ - private static int parseNumericFontWeight(String fontWeightString) { - // This should be much faster than using regex to verify input and Integer.parseInt - return fontWeightString.length() == 3 - && fontWeightString.endsWith("00") - && fontWeightString.charAt(0) <= '9' - && fontWeightString.charAt(0) >= '1' - ? 100 * (fontWeightString.charAt(0) - '0') - : -1; - } - - // TODO T63645393 remove this from here and add support to RTL - private YogaDirection getLayoutDirection() { - return YogaDirection.LTR; - } - - public float getBottomPadding() { - return getPaddingProp(ViewProps.PADDING_BOTTOM); - } - - public float getLeftPadding() { - return getPaddingProp(ViewProps.PADDING_LEFT); - } - - public float getStartPadding() { - return getPaddingProp(ViewProps.PADDING_START); - } - - public float getEndPadding() { - return getPaddingProp(ViewProps.PADDING_END); - } - - public float getTopPadding() { - return getPaddingProp(ViewProps.PADDING_TOP); - } - - public float getRightPadding() { - return getPaddingProp(ViewProps.PADDING_RIGHT); - } - - private float getPaddingProp(String paddingType) { - if (mProps.hasKey(ViewProps.PADDING)) { - return PixelUtil.toPixelFromDIP(getFloatProp(ViewProps.PADDING, 0f)); + public static int getHyphenationFrequency(@Nullable String hyphenationFrequency) { + int androidHyphenationFrequency = DEFAULT_HYPHENATION_FREQUENCY; + if (hyphenationFrequency != null) { + switch (hyphenationFrequency) { + case "none": + androidHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; + break; + case "normal": + androidHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL; + break; + default: + androidHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_FULL; + break; + } } - - return PixelUtil.toPixelFromDIP(getFloatProp(paddingType, 0f)); + return androidHyphenationFrequency; } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index 3b10c2c7d1c40..f31eee22e3006 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -24,12 +24,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; +import com.facebook.react.bridge.ReactNoCrashSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.common.build.ReactBuildConfig; -import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactAccessibilityDelegate; import com.facebook.react.uimanager.ReactStylesDiffMap; @@ -63,21 +64,20 @@ public class TextLayoutManager { private static final boolean DEFAULT_INCLUDE_FONT_PADDING = true; private static final String INCLUDE_FONT_PADDING_KEY = "includeFontPadding"; private static final String TEXT_BREAK_STRATEGY_KEY = "textBreakStrategy"; + private static final String HYPHENATION_FREQUENCY_KEY = "android_hyphenationFrequency"; private static final String MAXIMUM_NUMBER_OF_LINES_KEY = "maximumNumberOfLines"; - private static final LruCache sSpannableCache = - new LruCache<>(spannableCacheSize); - private static final LruCache sSpannableCacheV2 = + private static final LruCache sSpannableCache = new LruCache<>(spannableCacheSize); private static final ConcurrentHashMap sTagToSpannableCache = new ConcurrentHashMap<>(); public static boolean isRTL(ReadableMap attributedString) { ReadableArray fragments = attributedString.getArray("fragments"); - for (int i = 0, length = fragments.size(); i < length; i++) { + for (int i = 0; i < fragments.size(); i++) { ReadableMap fragment = fragments.getMap(i); - ReactStylesDiffMap map = new ReactStylesDiffMap(fragment.getMap("textAttributes")); - TextAttributeProps textAttributes = new TextAttributeProps(map); - return textAttributes.mLayoutDirection == LayoutDirection.RTL; + ReadableMap map = fragment.getMap("textAttributes"); + return TextAttributeProps.getLayoutDirection(map.getString(ViewProps.LAYOUT_DIRECTION)) + == LayoutDirection.RTL; } return false; } @@ -108,7 +108,8 @@ private static void buildSpannableFromFragment( // ReactRawText TextAttributeProps textAttributes = - new TextAttributeProps(new ReactStylesDiffMap(fragment.getMap("textAttributes"))); + TextAttributeProps.fromReadableMap( + new ReactStylesDiffMap(fragment.getMap("textAttributes"))); sb.append(TextTransform.apply(fragment.getString("string"), textAttributes.mTextTransform)); @@ -139,12 +140,10 @@ private static void buildSpannableFromFragment( new SetSpanOperation( start, end, new ReactBackgroundColorSpan(textAttributes.mBackgroundColor))); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (!Float.isNaN(textAttributes.getLetterSpacing())) { - ops.add( - new SetSpanOperation( - start, end, new CustomLetterSpacingSpan(textAttributes.getLetterSpacing()))); - } + if (!Float.isNaN(textAttributes.getLetterSpacing())) { + ops.add( + new SetSpanOperation( + start, end, new CustomLetterSpacingSpan(textAttributes.getLetterSpacing()))); } ops.add( new SetSpanOperation(start, end, new ReactAbsoluteSizeSpan(textAttributes.mFontSize))); @@ -197,25 +196,11 @@ public static Spannable getOrCreateSpannableForText( @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) { Spannable preparedSpannableText; - String attributedStringPayload = ""; - - boolean cacheByReadableNativeMap = - ReactFeatureFlags.enableSpannableCacheByReadableNativeMapEquality; - // TODO: T74600554 Cleanup this experiment once positive impact is confirmed in production - if (cacheByReadableNativeMap) { - synchronized (sSpannableCacheLock) { - preparedSpannableText = sSpannableCacheV2.get((ReadableNativeMap) attributedString); - if (preparedSpannableText != null) { - return preparedSpannableText; - } - } - } else { - attributedStringPayload = attributedString.toString(); - synchronized (sSpannableCacheLock) { - preparedSpannableText = sSpannableCache.get(attributedStringPayload); - if (preparedSpannableText != null) { - return preparedSpannableText; - } + + synchronized (sSpannableCacheLock) { + preparedSpannableText = sSpannableCache.get((ReadableNativeMap) attributedString); + if (preparedSpannableText != null) { + return preparedSpannableText; } } @@ -223,15 +208,10 @@ public static Spannable getOrCreateSpannableForText( createSpannableFromAttributedString( context, attributedString, reactTextViewManagerCallback); - if (cacheByReadableNativeMap) { - synchronized (sSpannableCacheLock) { - sSpannableCacheV2.put((ReadableNativeMap) attributedString, preparedSpannableText); - } - } else { - synchronized (sSpannableCacheLock) { - sSpannableCache.put(attributedStringPayload, preparedSpannableText); - } + synchronized (sSpannableCacheLock) { + sSpannableCache.put((ReadableNativeMap) attributedString, preparedSpannableText); } + return preparedSpannableText; } @@ -271,7 +251,8 @@ private static Layout createLayout( float width, YogaMeasureMode widthYogaMeasureMode, boolean includeFontPadding, - int textBreakStrategy) { + int textBreakStrategy, + int hyphenationFrequency) { Layout layout; int spanLength = text.length(); boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; @@ -302,18 +283,24 @@ private static Layout createLayout( .setLineSpacing(0.f, 1.f) .setIncludePad(includeFontPadding) .setBreakStrategy(textBreakStrategy) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setHyphenationFrequency(hyphenationFrequency) .build(); } - } else if (boring != null && (unconstrainedWidth || boring.width <= width)) { + int boringLayoutWidth = boring.width; + if (boring.width < 0) { + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException("Text width is invalid: " + boring.width)); + boringLayoutWidth = 0; + } + // Is used for single-line, boring text when the width is either unknown or bigger // than the width of the text. layout = BoringLayout.make( text, textPaint, - boring.width, + boringLayoutWidth, Layout.Alignment.ALIGN_NORMAL, 1.f, 0.f, @@ -339,7 +326,7 @@ private static Layout createLayout( .setLineSpacing(0.f, 1.f) .setIncludePad(includeFontPadding) .setBreakStrategy(textBreakStrategy) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); + .setHyphenationFrequency(hyphenationFrequency); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { builder.setUseLineSpacingFromFallbacks(true); @@ -392,20 +379,25 @@ public static long measureText( paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) : DEFAULT_INCLUDE_FONT_PADDING; + int hyphenationFrequency = + TextAttributeProps.getHyphenationFrequency( + paragraphAttributes.getString(HYPHENATION_FREQUENCY_KEY)); if (text == null) { throw new IllegalStateException("Spannable element has not been prepared in onBeforeLayout"); } BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); - float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; - - // technically, width should never be negative, but there is currently a bug in - boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; Layout layout = createLayout( - text, boring, width, widthYogaMeasureMode, includeFontPadding, textBreakStrategy); + text, + boring, + width, + widthYogaMeasureMode, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency); int maximumNumberOfLines = paragraphAttributes.hasKey(MAXIMUM_NUMBER_OF_LINES_KEY) @@ -511,16 +503,16 @@ public static long measureText( // The attachment array returns the positions of each of the attachments as attachmentsPositions[attachmentPosition] = - PixelUtil.toSPFromPixel(placeholderTopPosition); + PixelUtil.toDIPFromPixel(placeholderTopPosition); attachmentsPositions[attachmentPosition + 1] = - PixelUtil.toSPFromPixel(placeholderLeftPosition); + PixelUtil.toDIPFromPixel(placeholderLeftPosition); attachmentIndex++; } } } - float widthInSP = PixelUtil.toSPFromPixel(calculatedWidth); - float heightInSP = PixelUtil.toSPFromPixel(calculatedHeight); + float widthInSP = PixelUtil.toDIPFromPixel(calculatedWidth); + float heightInSP = PixelUtil.toDIPFromPixel(calculatedHeight); if (ENABLE_MEASURE_LOGGING) { FLog.e( @@ -557,10 +549,19 @@ public static WritableArray measureLines( paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) : DEFAULT_INCLUDE_FONT_PADDING; + int hyphenationFrequency = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(HYPHENATION_FREQUENCY_KEY)); Layout layout = createLayout( - text, boring, width, YogaMeasureMode.EXACTLY, includeFontPadding, textBreakStrategy); + text, + boring, + width, + YogaMeasureMode.EXACTLY, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency); return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java new file mode 100644 index 0000000000000..ee86e49d97e42 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java @@ -0,0 +1,616 @@ +/* + * Copyright (c) Facebook, Inc. and its 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 static com.facebook.react.views.text.TextAttributeProps.UNSET; + +import android.content.Context; +import android.os.Build; +import android.text.BoringLayout; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.LayoutDirection; +import android.util.LruCache; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; +import com.facebook.react.bridge.ReactNoCrashSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.uimanager.ReactAccessibilityDelegate; +import com.facebook.yoga.YogaConstants; +import com.facebook.yoga.YogaMeasureMode; +import com.facebook.yoga.YogaMeasureOutput; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** Class responsible of creating {@link Spanned} object for the JS representation of Text */ +public class TextLayoutManagerMapBuffer { + + // constants for AttributedString serialization + public static final short AS_KEY_HASH = 0; + public static final short AS_KEY_STRING = 1; + public static final short AS_KEY_FRAGMENTS = 2; + public static final short AS_KEY_CACHE_ID = 3; + + // constants for Fragment serialization + public static final short FR_KEY_STRING = 0; + public static final short FR_KEY_REACT_TAG = 1; + public static final short FR_KEY_IS_ATTACHMENT = 2; + public static final short FR_KEY_WIDTH = 3; + public static final short FR_KEY_HEIGHT = 4; + public static final short FR_KEY_TEXT_ATTRIBUTES = 5; + + // constants for ParagraphAttributes serialization + public static final short PA_KEY_MAX_NUMBER_OF_LINES = 0; + public static final short PA_KEY_ELLIPSIZE_MODE = 1; + public static final short PA_KEY_TEXT_BREAK_STRATEGY = 2; + public static final short PA_KEY_ADJUST_FONT_SIZE_TO_FIT = 3; + public static final short PA_KEY_INCLUDE_FONT_PADDING = 4; + public static final short PA_KEY_HYPHENATION_FREQUENCY = 5; + + private static final boolean ENABLE_MEASURE_LOGGING = ReactBuildConfig.DEBUG && false; + + private static final String TAG = TextLayoutManagerMapBuffer.class.getSimpleName(); + + // It's important to pass the ANTI_ALIAS_FLAG flag to the constructor rather than setting it + // later by calling setFlags. This is because the latter approach triggers a bug on Android 4.4.2. + // The bug is that unicode emoticons aren't measured properly which causes text to be clipped. + private static final TextPaint sTextPaintInstance = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + + // Specifies the amount of spannable that are stored into the {@link sSpannableCache}. + private static final short spannableCacheSize = 100; + + private static final String INLINE_VIEW_PLACEHOLDER = "0"; + + private static final Object sSpannableCacheLock = new Object(); + private static final boolean DEFAULT_INCLUDE_FONT_PADDING = true; + private static final LruCache sSpannableCache = + new LruCache<>(spannableCacheSize); + private static final ConcurrentHashMap sTagToSpannableCache = + new ConcurrentHashMap<>(); + + public static void setCachedSpannabledForTag(int reactTag, @NonNull Spannable sp) { + if (ENABLE_MEASURE_LOGGING) { + FLog.e(TAG, "Set cached spannable for tag[" + reactTag + "]: " + sp.toString()); + } + sTagToSpannableCache.put(reactTag, sp); + } + + public static void deleteCachedSpannableForTag(int reactTag) { + if (ENABLE_MEASURE_LOGGING) { + FLog.e(TAG, "Delete cached spannable for tag[" + reactTag + "]"); + } + sTagToSpannableCache.remove(reactTag); + } + + public static boolean isRTL(ReadableMapBuffer attributedString) { + ReadableMapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS); + if (fragments.getCount() == 0) { + return false; + } + + ReadableMapBuffer fragment = fragments.getMapBuffer((short) 0); + ReadableMapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES); + return TextAttributeProps.getLayoutDirection( + textAttributes.getString(TextAttributeProps.TA_KEY_LAYOUT_DIRECTION)) + == LayoutDirection.RTL; + } + + private static void buildSpannableFromFragment( + Context context, + ReadableMapBuffer fragments, + SpannableStringBuilder sb, + List ops) { + + for (short i = 0, length = fragments.getCount(); i < length; i++) { + ReadableMapBuffer fragment = fragments.getMapBuffer(i); + int start = sb.length(); + + TextAttributeProps textAttributes = + TextAttributeProps.fromReadableMapBuffer(fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES)); + + sb.append( + TextTransform.apply(fragment.getString(FR_KEY_STRING), textAttributes.mTextTransform)); + + int end = sb.length(); + int reactTag = + fragment.hasKey(FR_KEY_REACT_TAG) ? fragment.getInt(FR_KEY_REACT_TAG) : View.NO_ID; + if (fragment.hasKey(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) { + float width = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_WIDTH)); + float height = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_HEIGHT)); + ops.add( + new SetSpanOperation( + sb.length() - INLINE_VIEW_PLACEHOLDER.length(), + sb.length(), + new TextInlineViewPlaceholderSpan(reactTag, (int) width, (int) height))); + } else if (end >= start) { + if (ReactAccessibilityDelegate.AccessibilityRole.LINK.equals( + textAttributes.mAccessibilityRole)) { + ops.add( + new SetSpanOperation( + start, end, new ReactClickableSpan(reactTag, textAttributes.mColor))); + } else if (textAttributes.mIsColorSet) { + ops.add( + new SetSpanOperation( + start, end, new ReactForegroundColorSpan(textAttributes.mColor))); + } + if (textAttributes.mIsBackgroundColorSet) { + ops.add( + new SetSpanOperation( + start, end, new ReactBackgroundColorSpan(textAttributes.mBackgroundColor))); + } + if (!Float.isNaN(textAttributes.getLetterSpacing())) { + ops.add( + new SetSpanOperation( + start, end, new CustomLetterSpacingSpan(textAttributes.getLetterSpacing()))); + } + ops.add( + new SetSpanOperation(start, end, new ReactAbsoluteSizeSpan(textAttributes.mFontSize))); + if (textAttributes.mFontStyle != UNSET + || textAttributes.mFontWeight != UNSET + || textAttributes.mFontFamily != null) { + ops.add( + new SetSpanOperation( + start, + end, + new CustomStyleSpan( + textAttributes.mFontStyle, + textAttributes.mFontWeight, + textAttributes.mFontFeatureSettings, + textAttributes.mFontFamily, + context.getAssets()))); + } + if (textAttributes.mIsUnderlineTextDecorationSet) { + ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan())); + } + if (textAttributes.mIsLineThroughTextDecorationSet) { + ops.add(new SetSpanOperation(start, end, new ReactStrikethroughSpan())); + } + if (textAttributes.mTextShadowOffsetDx != 0 || textAttributes.mTextShadowOffsetDy != 0) { + ops.add( + new SetSpanOperation( + start, + end, + new ShadowStyleSpan( + textAttributes.mTextShadowOffsetDx, + textAttributes.mTextShadowOffsetDy, + textAttributes.mTextShadowRadius, + textAttributes.mTextShadowColor))); + } + if (!Float.isNaN(textAttributes.getEffectiveLineHeight())) { + ops.add( + new SetSpanOperation( + start, end, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight()))); + } + + ops.add(new SetSpanOperation(start, end, new ReactTagSpan(reactTag))); + } + } + } + + // public because both ReactTextViewManager and ReactTextInputManager need to use this + public static Spannable getOrCreateSpannableForText( + Context context, + ReadableMapBuffer attributedString, + @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) { + + Spannable preparedSpannableText; + + synchronized (sSpannableCacheLock) { + preparedSpannableText = sSpannableCache.get(attributedString); + if (preparedSpannableText != null) { + return preparedSpannableText; + } + } + + preparedSpannableText = + createSpannableFromAttributedString( + context, attributedString, reactTextViewManagerCallback); + + synchronized (sSpannableCacheLock) { + sSpannableCache.put(attributedString, preparedSpannableText); + } + + return preparedSpannableText; + } + + private static Spannable createSpannableFromAttributedString( + Context context, + ReadableMapBuffer attributedString, + @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) { + + SpannableStringBuilder sb = new SpannableStringBuilder(); + + // The {@link SpannableStringBuilder} implementation require setSpan operation to be called + // up-to-bottom, otherwise all the spannables that are within the region for which one may set + // a new spannable will be wiped out + List ops = new ArrayList<>(); + + buildSpannableFromFragment(context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS), sb, ops); + + // TODO T31905686: add support for inline Images + // While setting the Spans on the final text, we also check whether any of them are images. + int priority = 0; + for (SetSpanOperation op : ops) { + // Actual order of calling {@code execute} does NOT matter, + // but the {@code priority} DOES matter. + op.execute(sb, priority); + priority++; + } + + if (reactTextViewManagerCallback != null) { + reactTextViewManagerCallback.onPostProcessSpannable(sb); + } + return sb; + } + + private static Layout createLayout( + Spannable text, + BoringLayout.Metrics boring, + float width, + YogaMeasureMode widthYogaMeasureMode, + boolean includeFontPadding, + int textBreakStrategy, + int hyphenationFrequency) { + Layout layout; + int spanLength = text.length(); + boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; + TextPaint textPaint = sTextPaintInstance; + float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; + + if (boring == null + && (unconstrainedWidth + || (!YogaConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { + // Is used when the width is not known and the text is not boring, ie. if it contains + // unicode characters. + + int hintWidth = (int) Math.ceil(desiredWidth); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + layout = + new StaticLayout( + text, + textPaint, + hintWidth, + Layout.Alignment.ALIGN_NORMAL, + 1.f, + 0.f, + includeFontPadding); + } else { + layout = + StaticLayout.Builder.obtain(text, 0, spanLength, textPaint, hintWidth) + .setAlignment(Layout.Alignment.ALIGN_NORMAL) + .setLineSpacing(0.f, 1.f) + .setIncludePad(includeFontPadding) + .setBreakStrategy(textBreakStrategy) + .setHyphenationFrequency(hyphenationFrequency) + .build(); + } + + } else if (boring != null && (unconstrainedWidth || boring.width <= width)) { + int boringLayoutWidth = boring.width; + if (boring.width < 0) { + ReactSoftExceptionLogger.logSoftException( + TAG, new ReactNoCrashSoftException("Text width is invalid: " + boring.width)); + boringLayoutWidth = 0; + } + // Is used for single-line, boring text when the width is either unknown or bigger + // than the width of the text. + layout = + BoringLayout.make( + text, + textPaint, + boringLayoutWidth, + Layout.Alignment.ALIGN_NORMAL, + 1.f, + 0.f, + boring, + includeFontPadding); + } else { + // Is used for multiline, boring text and the width is known. + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + layout = + new StaticLayout( + text, + textPaint, + (int) width, + Layout.Alignment.ALIGN_NORMAL, + 1.f, + 0.f, + includeFontPadding); + } else { + StaticLayout.Builder builder = + StaticLayout.Builder.obtain(text, 0, spanLength, textPaint, (int) width) + .setAlignment(Layout.Alignment.ALIGN_NORMAL) + .setLineSpacing(0.f, 1.f) + .setIncludePad(includeFontPadding) + .setBreakStrategy(textBreakStrategy) + .setHyphenationFrequency(hyphenationFrequency); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + builder.setUseLineSpacingFromFallbacks(true); + } + + layout = builder.build(); + } + } + return layout; + } + + public static long measureText( + Context context, + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, + float width, + YogaMeasureMode widthYogaMeasureMode, + float height, + YogaMeasureMode heightYogaMeasureMode, + ReactTextViewManagerCallback reactTextViewManagerCallback, + @Nullable float[] attachmentsPositions) { + + // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) + TextPaint textPaint = sTextPaintInstance; + Spannable text; + if (attributedString.hasKey(AS_KEY_CACHE_ID)) { + int cacheId = attributedString.getInt(AS_KEY_CACHE_ID); + if (ENABLE_MEASURE_LOGGING) { + FLog.e(TAG, "Get cached spannable for cacheId[" + cacheId + "]"); + } + if (sTagToSpannableCache.containsKey(cacheId)) { + text = sTagToSpannableCache.get(cacheId); + if (ENABLE_MEASURE_LOGGING) { + FLog.e(TAG, "Text for spannable found for cacheId[" + cacheId + "]: " + text.toString()); + } + } else { + if (ENABLE_MEASURE_LOGGING) { + FLog.e(TAG, "No cached spannable found for cacheId[" + cacheId + "]"); + } + return 0; + } + } else { + text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); + } + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); + boolean includeFontPadding = + paragraphAttributes.hasKey(PA_KEY_INCLUDE_FONT_PADDING) + ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) + : DEFAULT_INCLUDE_FONT_PADDING; + int hyphenationFrequency = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_HYPHENATION_FREQUENCY)); + + if (text == null) { + throw new IllegalStateException("Spannable element has not been prepared in onBeforeLayout"); + } + + BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); + float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; + + // technically, width should never be negative, but there is currently a bug in + boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; + + Layout layout = + createLayout( + text, + boring, + width, + widthYogaMeasureMode, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency); + + int maximumNumberOfLines = + paragraphAttributes.hasKey(PA_KEY_MAX_NUMBER_OF_LINES) + ? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) + : UNSET; + + int calculatedLineCount = + maximumNumberOfLines == UNSET || maximumNumberOfLines == 0 + ? layout.getLineCount() + : Math.min(maximumNumberOfLines, layout.getLineCount()); + + // Instead of using `layout.getWidth()` (which may yield a significantly larger width for + // text that is wrapping), compute width using the longest line. + float calculatedWidth = 0; + if (widthYogaMeasureMode == YogaMeasureMode.EXACTLY) { + calculatedWidth = width; + } else { + for (int lineIndex = 0; lineIndex < calculatedLineCount; lineIndex++) { + float lineWidth = layout.getLineWidth(lineIndex); + if (lineWidth > calculatedWidth) { + calculatedWidth = lineWidth; + } + } + if (widthYogaMeasureMode == YogaMeasureMode.AT_MOST && calculatedWidth > width) { + calculatedWidth = width; + } + } + + float calculatedHeight = height; + if (heightYogaMeasureMode != YogaMeasureMode.EXACTLY) { + calculatedHeight = layout.getLineBottom(calculatedLineCount - 1); + if (heightYogaMeasureMode == YogaMeasureMode.AT_MOST && calculatedHeight > height) { + calculatedHeight = height; + } + } + + // Calculate the positions of the attachments (views) that will be rendered inside the + // Spanned Text. The following logic is only executed when a text contains views inside. + // This follows a similar logic than used in pre-fabric (see ReactTextView.onLayout method). + int attachmentIndex = 0; + int lastAttachmentFoundInSpan; + for (int i = 0; i < text.length(); i = lastAttachmentFoundInSpan) { + lastAttachmentFoundInSpan = + text.nextSpanTransition(i, text.length(), TextInlineViewPlaceholderSpan.class); + TextInlineViewPlaceholderSpan[] placeholders = + text.getSpans(i, lastAttachmentFoundInSpan, TextInlineViewPlaceholderSpan.class); + for (TextInlineViewPlaceholderSpan placeholder : placeholders) { + int start = text.getSpanStart(placeholder); + int line = layout.getLineForOffset(start); + boolean isLineTruncated = layout.getEllipsisCount(line) > 0; + // This truncation check works well on recent versions of Android (tested on 5.1.1 and + // 6.0.1) but not on Android 4.4.4. The reason is that getEllipsisCount is buggy on + // Android 4.4.4. Specifically, it incorrectly returns 0 if an inline view is the + // first thing to be truncated. + if (!(isLineTruncated && start >= layout.getLineStart(line) + layout.getEllipsisStart(line)) + || start >= layout.getLineEnd(line)) { + float placeholderWidth = placeholder.getWidth(); + float placeholderHeight = placeholder.getHeight(); + // Calculate if the direction of the placeholder character is Right-To-Left. + boolean isRtlChar = layout.isRtlCharAt(start); + boolean isRtlParagraph = layout.getParagraphDirection(line) == Layout.DIR_RIGHT_TO_LEFT; + float placeholderLeftPosition; + // There's a bug on Samsung devices where calling getPrimaryHorizontal on + // the last offset in the layout will result in an endless loop. Work around + // this bug by avoiding getPrimaryHorizontal in that case. + if (start == text.length() - 1) { + placeholderLeftPosition = + isRtlParagraph + // Equivalent to `layout.getLineLeft(line)` but `getLineLeft` returns + // incorrect + // values when the paragraph is RTL and `setSingleLine(true)`. + ? calculatedWidth - layout.getLineWidth(line) + : layout.getLineRight(line) - placeholderWidth; + } else { + // The direction of the paragraph may not be exactly the direction the string is + // heading + // in at the + // position of the placeholder. So, if the direction of the character is the same + // as the + // paragraph + // use primary, secondary otherwise. + boolean characterAndParagraphDirectionMatch = isRtlParagraph == isRtlChar; + placeholderLeftPosition = + characterAndParagraphDirectionMatch + ? layout.getPrimaryHorizontal(start) + : layout.getSecondaryHorizontal(start); + if (isRtlParagraph) { + // Adjust `placeholderLeftPosition` to work around an Android bug. + // The bug is when the paragraph is RTL and `setSingleLine(true)`, some layout + // methods such as `getPrimaryHorizontal`, `getSecondaryHorizontal`, and + // `getLineRight` return incorrect values. Their return values seem to be off + // by the same number of pixels so subtracting these values cancels out the + // error. + // + // The result is equivalent to bugless versions of + // `getPrimaryHorizontal`/`getSecondaryHorizontal`. + placeholderLeftPosition = + calculatedWidth - (layout.getLineRight(line) - placeholderLeftPosition); + } + if (isRtlChar) { + placeholderLeftPosition -= placeholderWidth; + } + } + // Vertically align the inline view to the baseline of the line of text. + float placeholderTopPosition = layout.getLineBaseline(line) - placeholderHeight; + int attachmentPosition = attachmentIndex * 2; + + // The attachment array returns the positions of each of the attachments as + attachmentsPositions[attachmentPosition] = + PixelUtil.toDIPFromPixel(placeholderTopPosition); + attachmentsPositions[attachmentPosition + 1] = + PixelUtil.toDIPFromPixel(placeholderLeftPosition); + attachmentIndex++; + } + } + } + + float widthInSP = PixelUtil.toDIPFromPixel(calculatedWidth); + float heightInSP = PixelUtil.toDIPFromPixel(calculatedHeight); + + if (ENABLE_MEASURE_LOGGING) { + FLog.e( + TAG, + "TextMeasure call ('" + + text + + "'): w: " + + calculatedWidth + + " px - h: " + + calculatedHeight + + " px - w : " + + widthInSP + + " sp - h: " + + heightInSP + + " sp"); + } + + return YogaMeasureOutput.make(widthInSP, heightInSP); + } + + public static WritableArray measureLines( + @NonNull Context context, + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, + float width) { + + TextPaint textPaint = sTextPaintInstance; + Spannable text = getOrCreateSpannableForText(context, attributedString, null); + BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); + boolean includeFontPadding = + paragraphAttributes.hasKey(PA_KEY_INCLUDE_FONT_PADDING) + ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) + : DEFAULT_INCLUDE_FONT_PADDING; + int hyphenationFrequency = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_HYPHENATION_FREQUENCY)); + + Layout layout = + createLayout( + text, + boring, + width, + YogaMeasureMode.EXACTLY, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency); + return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); + } + + // TODO T31905686: This class should be private + public static class SetSpanOperation { + protected int start, end; + protected ReactSpan what; + + public SetSpanOperation(int start, int end, ReactSpan what) { + this.start = start; + this.end = end; + this.what = what; + } + + public void execute(Spannable sb, int priority) { + // All spans will automatically extend to the right of the text, but not the left - except + // for spans that start at the beginning of the text. + int spanFlags = Spannable.SPAN_EXCLUSIVE_INCLUSIVE; + if (start == 0) { + spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE; + } + + spanFlags &= ~Spannable.SPAN_PRIORITY; + spanFlags |= (priority << Spannable.SPAN_PRIORITY_SHIFT) & Spannable.SPAN_PRIORITY; + + sb.setSpan(what, start, end, spanFlags); + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TypefaceStyle.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TypefaceStyle.java new file mode 100644 index 0000000000000..3119ae63afd64 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/TypefaceStyle.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.Typeface; +import android.os.Build; +import com.facebook.infer.annotation.Nullsafe; + +/** Responsible for normalizing style and numeric weight for backward compatibility. */ +@Nullsafe(Nullsafe.Mode.LOCAL) +class TypefaceStyle { + + public static final int BOLD = 700; + public static final int NORMAL = 400; + + private static final int MIN_WEIGHT = 1; + private static final int MAX_WEIGHT = 1000; + + private final boolean mItalic; + private final int mWeight; + + public TypefaceStyle(int weight, boolean italic) { + mItalic = italic; + mWeight = weight == ReactBaseTextShadowNode.UNSET ? NORMAL : weight; + } + + public TypefaceStyle(int style) { + if (style == ReactBaseTextShadowNode.UNSET) { + style = Typeface.NORMAL; + } + + mItalic = (style & Typeface.ITALIC) != 0; + mWeight = (style & Typeface.BOLD) != 0 ? BOLD : NORMAL; + } + + /** + * If `weight` is supplied, it will be combined with the italic bit from `style`. Otherwise, any + * existing weight bit in `style` will be used. + */ + public TypefaceStyle(int style, int weight) { + if (style == ReactBaseTextShadowNode.UNSET) { + style = Typeface.NORMAL; + } + + mItalic = (style & Typeface.ITALIC) != 0; + mWeight = + weight == ReactBaseTextShadowNode.UNSET + ? (style & Typeface.BOLD) != 0 ? BOLD : NORMAL + : weight; + } + + public int getNearestStyle() { + if (mWeight < BOLD) { + return mItalic ? Typeface.ITALIC : Typeface.NORMAL; + } else { + return mItalic ? Typeface.BOLD_ITALIC : Typeface.BOLD; + } + } + + public Typeface apply(Typeface typeface) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return Typeface.create(typeface, getNearestStyle()); + } else { + return Typeface.create(typeface, mWeight, mItalic); + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK index fef31ebea6c4a..38985bbf96b44 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "frescosupport", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK index 3b408b3d319e6..b8de4f2fe8949 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "textinput", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, @@ -14,6 +15,7 @@ rn_android_library( react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/jsr-305:jsr-305"), + react_native_dep("third-party/android/androidx:autofill"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java index 432347821a9a3..9d0d205a5f23b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactContentSizeChangedEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by EditText native view when content size changes. */ public class ReactContentSizeChangedEvent extends Event { @@ -20,8 +20,14 @@ public class ReactContentSizeChangedEvent extends Event { private float mContentWidth; private float mContentHeight; + @Deprecated public ReactContentSizeChangedEvent(int viewId, float contentSizeWidth, float contentSizeHeight) { - super(viewId); + this(-1, viewId, contentSizeWidth, contentSizeHeight); + } + + public ReactContentSizeChangedEvent( + int surfaceId, int viewId, float contentSizeWidth, float contentSizeHeight) { + super(surfaceId, viewId); mContentWidth = contentSizeWidth; mContentHeight = contentSizeHeight; } @@ -31,12 +37,9 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); WritableMap contentSize = Arguments.createMap(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index 058e154849dfc..84d5942d2cfc1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -36,15 +36,16 @@ import android.view.inputmethod.InputMethodManager; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatEditText; -import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.uimanager.FabricViewStateManager; +import com.facebook.react.uimanager.ReactAccessibilityDelegate; import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.views.text.CustomLetterSpacingSpan; import com.facebook.react.views.text.CustomLineHeightSpan; import com.facebook.react.views.text.CustomStyleSpan; @@ -106,8 +107,8 @@ public class ReactEditText extends AppCompatEditText private TextAttributes mTextAttributes; private boolean mTypefaceDirty = false; private @Nullable String mFontFamily = null; - private int mFontWeight = ReactTypefaceUtils.UNSET; - private int mFontStyle = ReactTypefaceUtils.UNSET; + private int mFontWeight = UNSET; + private int mFontStyle = UNSET; private boolean mAutoFocus = false; private boolean mDidAttachToWindow = false; @@ -119,6 +120,7 @@ public class ReactEditText extends AppCompatEditText protected boolean mIsSettingTextFromState = false; private static final KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard(); + private @Nullable EventDispatcher mEventDispatcher; public ReactEditText(Context context) { super(context); @@ -153,7 +155,7 @@ public ReactEditText(Context context) { ViewCompat.setAccessibilityDelegate( this, - new AccessibilityDelegateCompat() { + new ReactAccessibilityDelegate() { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == AccessibilityNodeInfo.ACTION_CLICK) { @@ -247,7 +249,8 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { InputConnection inputConnection = super.onCreateInputConnection(outAttrs); if (inputConnection != null && mOnKeyPress) { inputConnection = - new ReactEditTextInputConnectionWrapper(inputConnection, reactContext, this); + new ReactEditTextInputConnectionWrapper( + inputConnection, reactContext, this, mEventDispatcher); } if (isMultiline() && getBlurOnSubmit()) { @@ -328,10 +331,20 @@ public void maybeSetSelection(int eventCounter, int start, int end) { } if (start != UNSET && end != UNSET) { + // clamp selection values for safety + start = clampToTextLength(start); + end = clampToTextLength(end); + setSelection(start, end); } } + private int clampToTextLength(int value) { + int textLength = getText() == null ? 0 : getText().length(); + + return Math.max(0, Math.min(value, textLength)); + } + @Override public void setSelection(int start, int end) { if (DEBUG_MODE) { @@ -654,12 +667,10 @@ private void addSpansForMeasurement(Spannable spannable) { List ops = new ArrayList<>(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (!Float.isNaN(mTextAttributes.getLetterSpacing())) { - ops.add( - new TextLayoutManager.SetSpanOperation( - start, end, new CustomLetterSpacingSpan(mTextAttributes.getLetterSpacing()))); - } + if (!Float.isNaN(mTextAttributes.getLetterSpacing())) { + ops.add( + new TextLayoutManager.SetSpanOperation( + start, end, new CustomLetterSpacingSpan(mTextAttributes.getLetterSpacing()))); } ops.add( new TextLayoutManager.SetSpanOperation( @@ -733,8 +744,9 @@ private void setIntrinsicContentSize() { // wrapper 100% of the time. // Since the LocalData object is constructed by getting values from the underlying EditText // view, we don't need to construct one or apply it at all - it provides no use in Fabric. - if (!mFabricViewStateManager.hasStateWrapper()) { - ReactContext reactContext = getReactContext(this); + ReactContext reactContext = getReactContext(this); + + if (!mFabricViewStateManager.hasStateWrapper() && !reactContext.isBridgeless()) { final ReactTextInputLocalData localData = new ReactTextInputLocalData(this); UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); if (uiManager != null) { @@ -947,11 +959,9 @@ protected void applyTextAttributes() { // `Float.NaN`. setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextAttributes.getEffectiveFontSize()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing(); - if (!Float.isNaN(effectiveLetterSpacing)) { - setLetterSpacing(effectiveLetterSpacing); - } + float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing(); + if (!Float.isNaN(effectiveLetterSpacing)) { + setLetterSpacing(effectiveLetterSpacing); } } @@ -1025,7 +1035,7 @@ private void updateCachedSpannable(boolean resetStyles) { try { sb.append(currentText.subSequence(0, currentText.length())); } catch (IndexOutOfBoundsException e) { - ReactSoftException.logSoftException(TAG, e); + ReactSoftExceptionLogger.logSoftException(TAG, e); } } @@ -1047,6 +1057,10 @@ private void updateCachedSpannable(boolean resetStyles) { TextLayoutManager.setCachedSpannabledForTag(getId(), sb); } + void setEventDispatcher(@Nullable EventDispatcher eventDispatcher) { + mEventDispatcher = eventDispatcher; + } + /** * This class will redirect *TextChanged calls to the listeners only in the case where the text is * changed by the user, and not explicitly set by JS. diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditTextInputConnectionWrapper.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditTextInputConnectionWrapper.java index 47b4391c327f1..aa2f9a6462e52 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditTextInputConnectionWrapper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditTextInputConnectionWrapper.java @@ -12,9 +12,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.events.EventDispatcher; /** @@ -61,11 +59,12 @@ class ReactEditTextInputConnectionWrapper extends InputConnectionWrapper { private @Nullable String mKey = null; public ReactEditTextInputConnectionWrapper( - InputConnection target, final ReactContext reactContext, final ReactEditText editText) { + InputConnection target, + final ReactContext reactContext, + final ReactEditText editText, + EventDispatcher eventDispatcher) { super(target, false); - mEventDispatcher = - Assertions.assertNotNull(reactContext.getNativeModule(UIManagerModule.class)) - .getEventDispatcher(); + mEventDispatcher = eventDispatcher; mEditText = editText; } @@ -132,10 +131,13 @@ public boolean deleteSurroundingText(int beforeLength, int afterLength) { @Override public boolean sendKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { + boolean isNumberKey = event.getUnicodeChar() < 58 && event.getUnicodeChar() > 47; if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { dispatchKeyEvent(BACKSPACE_KEY_VALUE); } else if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) { dispatchKeyEvent(ENTER_KEY_VALUE); + } else if (isNumberKey) { + dispatchKeyEvent(String.valueOf(event.getNumber())); } } return super.sendKeyEvent(event); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextChangedEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextChangedEvent.java index a3c7e1cac12e0..f2cce90c50f03 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextChangedEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextChangedEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** * Event emitted by EditText native view when text changes. VisibleForTesting from {@link @@ -23,8 +23,13 @@ public class ReactTextChangedEvent extends Event { private String mText; private int mEventCount; + @Deprecated public ReactTextChangedEvent(int viewId, String text, int eventCount) { - super(viewId); + this(-1, viewId, text, eventCount); + } + + public ReactTextChangedEvent(int surfaceId, int viewId, String text, int eventCount) { + super(surfaceId, viewId); mText = text; mEventCount = eventCount; } @@ -34,12 +39,9 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putString("text", mText); eventData.putInt("eventCount", mEventCount); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputBlurEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputBlurEvent.java index 5a05bc4440742..cd6dfca8c39b1 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputBlurEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputBlurEvent.java @@ -7,18 +7,23 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by EditText native view when it loses focus. */ /* package */ class ReactTextInputBlurEvent extends Event { private static final String EVENT_NAME = "topBlur"; + @Deprecated public ReactTextInputBlurEvent(int viewId) { - super(viewId); + this(-1, viewId); + } + + public ReactTextInputBlurEvent(int surfaceId, int viewId) { + super(surfaceId, viewId); } @Override @@ -31,12 +36,9 @@ public boolean canCoalesce() { return false; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putInt("target", getViewTag()); return eventData; diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEndEditingEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEndEditingEvent.java index 597fa7d13350b..1fca01b9fa553 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEndEditingEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEndEditingEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** * Event emitted by EditText native view when text editing ends, because of the user leaving the @@ -22,8 +22,13 @@ class ReactTextInputEndEditingEvent extends Event private String mText; + @Deprecated public ReactTextInputEndEditingEvent(int viewId, String text) { - super(viewId); + this(-1, viewId, text); + } + + public ReactTextInputEndEditingEvent(int surfaceId, int viewId, String text) { + super(surfaceId, viewId); mText = text; } @@ -37,12 +42,9 @@ public boolean canCoalesce() { return false; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putInt("target", getViewTag()); eventData.putString("text", mText); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEvent.java index 0931078205593..2285fbfafb3e4 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** * Event emitted by EditText native view when text changes. VisibleForTesting from {@link @@ -25,9 +25,15 @@ public class ReactTextInputEvent extends Event { private int mRangeStart; private int mRangeEnd; + @Deprecated public ReactTextInputEvent( int viewId, String text, String previousText, int rangeStart, int rangeEnd) { - super(viewId); + this(-1, viewId, text, previousText, rangeStart, rangeEnd); + } + + public ReactTextInputEvent( + int surfaceId, int viewId, String text, String previousText, int rangeStart, int rangeEnd) { + super(surfaceId, viewId); mText = text; mPreviousText = previousText; mRangeStart = rangeStart; @@ -45,12 +51,9 @@ public boolean canCoalesce() { return false; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); WritableMap range = Arguments.createMap(); range.putDouble("start", mRangeStart); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputFocusEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputFocusEvent.java index 0bdaa6c19926f..61f7250e7cc7b 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputFocusEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputFocusEvent.java @@ -7,38 +7,40 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by EditText native view when it receives focus. */ /* package */ class ReactTextInputFocusEvent extends Event { private static final String EVENT_NAME = "topFocus"; + @Deprecated public ReactTextInputFocusEvent(int viewId) { - super(viewId); + this(-1, viewId); } - @Override - public String getEventName() { - return EVENT_NAME; + public ReactTextInputFocusEvent(int surfaceId, int viewId) { + super(surfaceId, viewId); } @Override - public boolean canCoalesce() { - return false; + public String getEventName() { + return EVENT_NAME; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putInt("target", getViewTag()); return eventData; } + + @Override + public boolean canCoalesce() { + return false; + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputKeyPressEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputKeyPressEvent.java index b4193e984eb5d..0b85f61802760 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputKeyPressEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputKeyPressEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by EditText native view when key pressed */ public class ReactTextInputKeyPressEvent extends Event { @@ -19,8 +19,13 @@ public class ReactTextInputKeyPressEvent extends Event { private String mKey; + @Deprecated ReactTextInputKeyPressEvent(int viewId, final String key) { - super(viewId); + this(-1, viewId, key); + } + + ReactTextInputKeyPressEvent(int surfaceId, int viewId, final String key) { + super(surfaceId, viewId); mKey = key; } @@ -29,21 +34,17 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public boolean canCoalesce() { - // We don't want to miss any textinput event, as event data is incremental. - return false; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putString("key", mKey); - return eventData; } + + @Override + public boolean canCoalesce() { + // We don't want to miss any textinput event, as event data is incremental. + return false; + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index 6d9ab0d39b6d4..0b6298fdd1f48 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -30,13 +30,14 @@ import android.widget.EditText; import android.widget.TextView; import androidx.annotation.Nullable; +import androidx.autofill.HintConstants; import androidx.core.content.ContextCompat; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; @@ -72,6 +73,7 @@ import com.facebook.react.views.text.TextTransform; import com.facebook.yoga.YogaConstants; import java.lang.reflect.Field; +import java.util.HashMap; import java.util.LinkedList; import java.util.Map; @@ -84,6 +86,51 @@ public class ReactTextInputManager extends BaseViewManager REACT_PROPS_AUTOFILL_HINTS_MAP = + new HashMap() { + { + put("birthdate-day", HintConstants.AUTOFILL_HINT_BIRTH_DATE_DAY); + put("birthdate-full", HintConstants.AUTOFILL_HINT_BIRTH_DATE_FULL); + put("birthdate-month", HintConstants.AUTOFILL_HINT_BIRTH_DATE_MONTH); + put("birthdate-year", HintConstants.AUTOFILL_HINT_BIRTH_DATE_YEAR); + put("cc-csc", HintConstants.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE); + put("cc-exp", HintConstants.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE); + put("cc-exp-day", HintConstants.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY); + put("cc-exp-month", HintConstants.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH); + put("cc-exp-year", HintConstants.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR); + put("cc-number", HintConstants.AUTOFILL_HINT_CREDIT_CARD_NUMBER); + put("email", HintConstants.AUTOFILL_HINT_EMAIL_ADDRESS); + put("gender", HintConstants.AUTOFILL_HINT_GENDER); + put("name", HintConstants.AUTOFILL_HINT_PERSON_NAME); + put("name-family", HintConstants.AUTOFILL_HINT_PERSON_NAME_FAMILY); + put("name-given", HintConstants.AUTOFILL_HINT_PERSON_NAME_GIVEN); + put("name-middle", HintConstants.AUTOFILL_HINT_PERSON_NAME_MIDDLE); + put("name-middle-initial", HintConstants.AUTOFILL_HINT_PERSON_NAME_MIDDLE_INITIAL); + put("name-prefix", HintConstants.AUTOFILL_HINT_PERSON_NAME_PREFIX); + put("name-suffix", HintConstants.AUTOFILL_HINT_PERSON_NAME_SUFFIX); + put("password", HintConstants.AUTOFILL_HINT_PASSWORD); + put("password-new", HintConstants.AUTOFILL_HINT_NEW_PASSWORD); + put("postal-address", HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS); + put("postal-address-country", HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_COUNTRY); + put( + "postal-address-extended", + HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_EXTENDED_ADDRESS); + put( + "postal-address-extended-postal-code", + HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_EXTENDED_POSTAL_CODE); + put("postal-address-locality", HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_LOCALITY); + put("postal-address-region", HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_REGION); + put("postal-code", HintConstants.AUTOFILL_HINT_POSTAL_CODE); + put("street-address", HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_STREET_ADDRESS); + put("sms-otp", HintConstants.AUTOFILL_HINT_SMS_OTP); + put("tel", HintConstants.AUTOFILL_HINT_PHONE_NUMBER); + put("tel-country-code", HintConstants.AUTOFILL_HINT_PHONE_COUNTRY_CODE); + put("tel-national", HintConstants.AUTOFILL_HINT_PHONE_NATIONAL); + put("tel-device", HintConstants.AUTOFILL_HINT_PHONE_NUMBER_DEVICE); + put("username", HintConstants.AUTOFILL_HINT_USERNAME); + put("username-new", HintConstants.AUTOFILL_HINT_NEW_USERNAME); + } + }; private static final int FOCUS_TEXT_INPUT = 1; private static final int BLUR_TEXT_INPUT = 2; @@ -108,8 +155,18 @@ public class ReactTextInputManager extends BaseViewManager { @@ -20,8 +20,14 @@ private int mSelectionStart; private int mSelectionEnd; + @Deprecated public ReactTextInputSelectionEvent(int viewId, int selectionStart, int selectionEnd) { - super(viewId); + this(-1, viewId, selectionStart, selectionEnd); + } + + public ReactTextInputSelectionEvent( + int surfaceId, int viewId, int selectionStart, int selectionEnd) { + super(surfaceId, viewId); mSelectionStart = selectionStart; mSelectionEnd = selectionEnd; } @@ -31,12 +37,9 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); WritableMap selectionData = Arguments.createMap(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSubmitEditingEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSubmitEditingEvent.java index fb717e70a23ab..0dee36ddfac75 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSubmitEditingEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSubmitEditingEvent.java @@ -7,10 +7,10 @@ package com.facebook.react.views.textinput; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Event emitted by EditText native view when the user submits the text. */ /* package */ class ReactTextInputSubmitEditingEvent @@ -20,8 +20,13 @@ private String mText; + @Deprecated public ReactTextInputSubmitEditingEvent(int viewId, String text) { - super(viewId); + this(-1, viewId, text); + } + + public ReactTextInputSubmitEditingEvent(int surfaceId, int viewId, String text) { + super(surfaceId, viewId); mText = text; } @@ -30,20 +35,17 @@ public String getEventName() { return EVENT_NAME; } + @Nullable @Override - public boolean canCoalesce() { - return false; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { + protected WritableMap getEventData() { WritableMap eventData = Arguments.createMap(); eventData.putInt("target", getViewTag()); eventData.putString("text", mText); return eventData; } + + @Override + public boolean canCoalesce() { + return false; + } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK index 24d0a3f2d916f..62b23d28001ac 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "unimplementedview", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK index 66386a0ffbfd5..8dc2755766272 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r rn_android_library( name = "view", srcs = glob(["*.java"]), + autoglob = False, is_androidx = True, labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/CanvasUtil.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/CanvasUtil.java new file mode 100644 index 0000000000000..f65353bb8dd91 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/CanvasUtil.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.view; + +import android.annotation.SuppressLint; +import android.graphics.Canvas; +import android.os.Build; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import javax.annotation.Nullable; + +/** + * Copied from + * Compose canvas utils + */ +public class CanvasUtil { + private CanvasUtil() {} + + private @Nullable static Method mReorderBarrierMethod = null; + private @Nullable static Method mInorderBarrierMethod = null; + private static boolean mOrderMethodsFetched = false; + + /** + * Enables Z support for the Canvas. The method is publicly available starting from API 29 and was + * hidden before, so we have to resort to reflection tricks to ensure we can use this API. + */ + @SuppressLint({"SoonBlockedPrivateApi", "PrivateApi"}) + public static void enableZ(Canvas canvas, boolean enable) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return; + } + + if (Build.VERSION.SDK_INT >= 29) { + if (enable) { + canvas.enableZ(); + } else { + canvas.disableZ(); + } + } else { + fetchOrderMethods(); + try { + if (enable && mReorderBarrierMethod != null) { + mReorderBarrierMethod.invoke(canvas); + } + if (!enable && mInorderBarrierMethod != null) { + mInorderBarrierMethod.invoke(canvas); + } + } catch (IllegalAccessException | InvocationTargetException ignore) { + // Do nothing + } + } + } + + private static void fetchOrderMethods() { + if (!mOrderMethodsFetched) { + try { + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) { + // use double reflection to avoid grey list on P + Method getDeclaredMethod = + Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class); + mReorderBarrierMethod = + (Method) getDeclaredMethod.invoke(Canvas.class, "insertReorderBarrier", new Class[0]); + mInorderBarrierMethod = + (Method) getDeclaredMethod.invoke(Canvas.class, "insertInorderBarrier", new Class[0]); + } else { + mReorderBarrierMethod = Canvas.class.getDeclaredMethod("insertReorderBarrier"); + mInorderBarrierMethod = Canvas.class.getDeclaredMethod("insertInorderBarrier"); + } + + if (mReorderBarrierMethod == null || mInorderBarrierMethod == null) { + return; + } + + mReorderBarrierMethod.setAccessible(true); + mInorderBarrierMethod.setAccessible(true); + } catch (IllegalAccessException ignore) { + // Do nothing + } catch (InvocationTargetException ignore) { + // Do nothing + } catch (NoSuchMethodException ignore) { + // Do nothing + } + mOrderMethodsFetched = true; + } + } +} diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java index 8f5f826733093..b4a3feb9418c8 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java @@ -65,11 +65,7 @@ private static int getAttrId(Context context, String attr) { } private static Drawable getDefaultThemeDrawable(Context context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return context.getResources().getDrawable(sResolveOutValue.resourceId, context.getTheme()); - } else { - return context.getResources().getDrawable(sResolveOutValue.resourceId); - } + return context.getResources().getDrawable(sResolveOutValue.resourceId, context.getTheme()); } private static RippleDrawable getRippleDrawable( diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java index 9065f4bc49fb3..5d19b87139243 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java @@ -89,6 +89,7 @@ private enum BorderStyle { private @Nullable Path mOuterClipPathForBorderRadius; private @Nullable Path mPathForBorderRadiusOutline; private @Nullable Path mPathForBorder; + private Path mPathForSingleBorder = new Path(); private @Nullable Path mCenterDrawPath; private @Nullable RectF mInnerClipTempRectForBorderRadius; private @Nullable RectF mOuterClipTempRectForBorderRadius; @@ -968,6 +969,14 @@ private void updatePathEffect() { mPaint.setPathEffect(mPathEffectForBorderStyle); } + private void updatePathEffect(int borderWidth) { + PathEffect pathEffectForBorderStyle = null; + if (mBorderStyle != null) { + pathEffectForBorderStyle = BorderStyle.getPathEffect(mBorderStyle, borderWidth); + } + mPaint.setPathEffect(pathEffectForBorderStyle); + } + /** For rounded borders we use default "borderWidth" property. */ public float getFullBorderWidth() { return (mBorderWidth != null && !YogaConstants.isUndefined(mBorderWidth.getRaw(Spacing.ALL))) @@ -1083,6 +1092,7 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { colorTop, colorRight, colorBottom); + if (fastBorderColor != 0) { if (Color.alpha(fastBorderColor) != 0) { // Border color is not transparent. @@ -1090,21 +1100,42 @@ private void drawRectangularBackgroundWithBorders(Canvas canvas) { int bottom = bounds.bottom; mPaint.setColor(fastBorderColor); + mPaint.setStyle(Paint.Style.STROKE); if (borderLeft > 0) { - int leftInset = left + borderLeft; - canvas.drawRect(left, top, leftInset, bottom - borderBottom, mPaint); + mPathForSingleBorder.reset(); + int width = Math.round(borderWidth.left); + updatePathEffect(width); + mPaint.setStrokeWidth(width); + mPathForSingleBorder.moveTo(left + width / 2, top); + mPathForSingleBorder.lineTo(left + width / 2, bottom); + canvas.drawPath(mPathForSingleBorder, mPaint); } if (borderTop > 0) { - int topInset = top + borderTop; - canvas.drawRect(left + borderLeft, top, right, topInset, mPaint); + mPathForSingleBorder.reset(); + int width = Math.round(borderWidth.top); + updatePathEffect(width); + mPaint.setStrokeWidth(width); + mPathForSingleBorder.moveTo(left, top + width / 2); + mPathForSingleBorder.lineTo(right, top + width / 2); + canvas.drawPath(mPathForSingleBorder, mPaint); } if (borderRight > 0) { - int rightInset = right - borderRight; - canvas.drawRect(rightInset, top + borderTop, right, bottom, mPaint); + mPathForSingleBorder.reset(); + int width = Math.round(borderWidth.right); + updatePathEffect(width); + mPaint.setStrokeWidth(width); + mPathForSingleBorder.moveTo(right - width / 2, top); + mPathForSingleBorder.lineTo(right - width / 2, bottom); + canvas.drawPath(mPathForSingleBorder, mPaint); } if (borderBottom > 0) { - int bottomInset = bottom - borderBottom; - canvas.drawRect(left, bottomInset, right - borderRight, bottom, mPaint); + mPathForSingleBorder.reset(); + int width = Math.round(borderWidth.bottom); + updatePathEffect(width); + mPaint.setStrokeWidth(width); + mPathForSingleBorder.moveTo(left, bottom - width / 2); + mPathForSingleBorder.lineTo(right, bottom - width / 2); + canvas.drawPath(mPathForSingleBorder, mPaint); } } } else { diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index 659a8afb341ed..8aedfecf77232 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -23,13 +23,14 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import android.view.ViewStructure; import android.view.animation.Animation; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReactNoCrashSoftException; +import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.config.ReactFeatureFlags; @@ -40,8 +41,10 @@ import com.facebook.react.uimanager.IllegalViewOperationException; import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PointerEvents; +import com.facebook.react.uimanager.ReactClippingProhibitedView; import com.facebook.react.uimanager.ReactClippingViewGroup; import com.facebook.react.uimanager.ReactClippingViewGroupHelper; +import com.facebook.react.uimanager.ReactOverflowView; import com.facebook.react.uimanager.ReactPointerEventsView; import com.facebook.react.uimanager.ReactZIndexedViewGroup; import com.facebook.react.uimanager.RootView; @@ -62,7 +65,8 @@ public class ReactViewGroup extends ViewGroup ReactClippingViewGroup, ReactPointerEventsView, ReactHitSlopView, - ReactZIndexedViewGroup { + ReactZIndexedViewGroup, + ReactOverflowView { private static final int ARRAY_CAPACITY_INCREMENT = 12; private static final int DEFAULT_BACKGROUND_COLOR = Color.TRANSPARENT; @@ -122,7 +126,7 @@ public void onLayoutChange( private @Nullable ReactViewBackgroundDrawable mReactBackgroundDrawable; private @Nullable OnInterceptTouchEventListener mOnInterceptTouchEventListener; private boolean mNeedsOffscreenAlphaCompositing = false; - private final ViewGroupDrawingOrderHelper mDrawingOrderHelper; + private @Nullable ViewGroupDrawingOrderHelper mDrawingOrderHelper = null; private @Nullable Path mPath; private int mLayoutDirection; private float mBackfaceOpacity = 1.f; @@ -131,7 +135,13 @@ public void onLayoutChange( public ReactViewGroup(Context context) { super(context); setClipChildren(false); - mDrawingOrderHelper = new ViewGroupDrawingOrderHelper(this); + } + + private ViewGroupDrawingOrderHelper getDrawingOrderHelper() { + if (mDrawingOrderHelper == null) { + mDrawingOrderHelper = new ViewGroupDrawingOrderHelper(this); + } + return mDrawingOrderHelper; } @Override @@ -414,9 +424,7 @@ private void updateSubviewClipStatus(View subview) { @Override public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { - return ReactFeatureFlags.clipChildRectsIfOverflowIsHidden - ? ReactClippingViewGroupHelper.getChildVisibleRectHelper(child, r, offset, this, mOverflow) - : super.getChildVisibleRect(child, r, offset); + return super.getChildVisibleRect(child, r, offset); } @Override @@ -439,11 +447,7 @@ private boolean customDrawOrderDisabled() { if (getId() == NO_ID) { return false; } - if (ViewUtil.getUIManagerType(getId()) != UIManagerType.FABRIC) { - return false; - } - - return ReactFeatureFlags.disableCustomDrawOrderFabric; + return ViewUtil.getUIManagerType(getId()) == UIManagerType.FABRIC; } @Override @@ -452,8 +456,8 @@ public void addView(View child, int index, ViewGroup.LayoutParams params) { // method. if (!customDrawOrderDisabled()) { - mDrawingOrderHelper.handleAddView(child); - setChildrenDrawingOrderEnabled(mDrawingOrderHelper.shouldEnableCustomDrawingOrder()); + getDrawingOrderHelper().handleAddView(child); + setChildrenDrawingOrderEnabled(getDrawingOrderHelper().shouldEnableCustomDrawingOrder()); } else { setChildrenDrawingOrderEnabled(false); } @@ -466,8 +470,8 @@ public void removeView(View view) { UiThreadUtil.assertOnUiThread(); if (!customDrawOrderDisabled()) { - mDrawingOrderHelper.handleRemoveView(view); - setChildrenDrawingOrderEnabled(mDrawingOrderHelper.shouldEnableCustomDrawingOrder()); + getDrawingOrderHelper().handleRemoveView(view); + setChildrenDrawingOrderEnabled(getDrawingOrderHelper().shouldEnableCustomDrawingOrder()); } else { setChildrenDrawingOrderEnabled(false); } @@ -480,8 +484,8 @@ public void removeViewAt(int index) { UiThreadUtil.assertOnUiThread(); if (!customDrawOrderDisabled()) { - mDrawingOrderHelper.handleRemoveView(getChildAt(index)); - setChildrenDrawingOrderEnabled(mDrawingOrderHelper.shouldEnableCustomDrawingOrder()); + getDrawingOrderHelper().handleRemoveView(getChildAt(index)); + setChildrenDrawingOrderEnabled(getDrawingOrderHelper().shouldEnableCustomDrawingOrder()); } else { setChildrenDrawingOrderEnabled(false); } @@ -491,16 +495,25 @@ public void removeViewAt(int index) { @Override protected int getChildDrawingOrder(int childCount, int index) { - return mDrawingOrderHelper.getChildDrawingOrder(childCount, index); + UiThreadUtil.assertOnUiThread(); + + if (!customDrawOrderDisabled()) { + return getDrawingOrderHelper().getChildDrawingOrder(childCount, index); + } else { + return index; + } } @Override public int getZIndexMappedChildIndex(int index) { - if (mDrawingOrderHelper.shouldEnableCustomDrawingOrder()) { - return mDrawingOrderHelper.getChildDrawingOrder(getChildCount(), index); - } else { - return index; + UiThreadUtil.assertOnUiThread(); + + if (!customDrawOrderDisabled() && getDrawingOrderHelper().shouldEnableCustomDrawingOrder()) { + return getDrawingOrderHelper().getChildDrawingOrder(getChildCount(), index); } + + // Fabric behavior + return index; } @Override @@ -509,8 +522,8 @@ public void updateDrawingOrder() { return; } - mDrawingOrderHelper.update(); - setChildrenDrawingOrderEnabled(mDrawingOrderHelper.shouldEnableCustomDrawingOrder()); + getDrawingOrderHelper().update(); + setChildrenDrawingOrderEnabled(getDrawingOrderHelper().shouldEnableCustomDrawingOrder()); invalidate(); } @@ -542,7 +555,7 @@ protected void dispatchSetPressed(boolean pressed) { } /*package*/ void addViewWithSubviewClippingEnabled( - View child, int index, ViewGroup.LayoutParams params) { + final View child, int index, ViewGroup.LayoutParams params) { Assertions.assertCondition(mRemoveClippedSubviews); Assertions.assertNotNull(mClippingRect); Assertions.assertNotNull(mAllChildren); @@ -557,6 +570,29 @@ protected void dispatchSetPressed(boolean pressed) { } updateSubviewClipStatus(mClippingRect, index, clippedSoFar); child.addOnLayoutChangeListener(mChildrenLayoutChangeListener); + + if (child instanceof ReactClippingProhibitedView) { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + if (!child.isShown()) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Child view has been added to Parent view in which it is clipped and not visible." + + " This is not legal for this particular child view. Child: [" + + child.getId() + + "] " + + child.toString() + + " Parent: [" + + getId() + + "] " + + toString())); + } + } + }); + } } /*package*/ void removeViewWithSubviewClippingEnabled(View view) { @@ -685,6 +721,7 @@ public void setOverflow(String overflow) { invalidate(); } + @Override public @Nullable String getOverflow() { return mOverflow; } @@ -702,48 +739,10 @@ private void updateBackgroundDrawable(Drawable drawable) { @Override protected void dispatchDraw(Canvas canvas) { - // TODO T78035906: delete this if we find the root-cause - int initialChildCount = getChildCount(); - try { dispatchOverflowDraw(canvas); super.dispatchDraw(canvas); } catch (NullPointerException | StackOverflowError e) { - // Catch errors and log additional diagnostics to logcat for debugging - FLog.e( - TAG, - "Exception thrown when executing ReactViewGroup.dispatchDraw method on ReactViewGroup[" - + getId() - + "]", - e); - - // Log all children of view, if any - int childCount = getChildCount(); - FLog.e(TAG, "Initial Child Count: %d / final: %d", initialChildCount, childCount); - FLog.e(TAG, "Child List:"); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - FLog.e( - TAG, - "Child #" - + i - + ": " - + (child != null ? child.getId() : -1337) - + " - " - + (child != null ? child.toString() : "")); - } - - // Log all ancestors of view - ViewParent viewParent = getParent(); - FLog.e(TAG, "Ancestor List:"); - while (viewParent != null) { - ViewGroup parentViewGroup = - (viewParent instanceof ViewGroup ? (ViewGroup) viewParent : null); - int parentViewGroupId = (parentViewGroup != null ? parentViewGroup.getId() : -1337); - FLog.e(TAG, "Ancestor[" + parentViewGroupId + "]: " + viewParent.toString()); - viewParent = viewParent.getParent(); - } - // Adding special exception management for StackOverflowError for logging purposes. // This will be removed in the future. RootView rootView = RootViewUtil.getRootView(ReactViewGroup.this); @@ -761,6 +760,23 @@ protected void dispatchDraw(Canvas canvas) { } } + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean drawWithZ = + child.getElevation() > 0 && ReactFeatureFlags.insertZReorderBarriersOnViewGroupChildren; + + if (drawWithZ) { + CanvasUtil.enableZ(canvas, true); + } + + boolean result = super.drawChild(canvas, child, drawingTime); + + if (drawWithZ) { + CanvasUtil.enableZ(canvas, false); + } + return result; + } + private void dispatchOverflowDraw(Canvas canvas) { if (mOverflow != null) { switch (mOverflow) { @@ -770,6 +786,7 @@ private void dispatchOverflowDraw(Canvas canvas) { } break; case ViewProps.HIDDEN: + case ViewProps.SCROLL: float left = 0f; float top = 0f; float right = getWidth(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index 66aacfcfd157d..1192b50f2e4cc 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -239,7 +239,9 @@ public void onClick(View v) { if (mEventDispatcher == null) { return; } - mEventDispatcher.dispatchEvent(new ViewGroupClickEvent(view.getId())); + mEventDispatcher.dispatchEvent( + new ViewGroupClickEvent( + UIManagerHelper.getSurfaceId(view.getContext()), view.getId())); } }); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ViewGroupClickEvent.java b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ViewGroupClickEvent.java index c328752a56417..d7a380802fbb0 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ViewGroupClickEvent.java +++ b/android/ReactAndroid/src/main/java/com/facebook/react/views/view/ViewGroupClickEvent.java @@ -7,16 +7,22 @@ package com.facebook.react.views.view; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** Represents a Click on the ReactViewGroup */ public class ViewGroupClickEvent extends Event { private static final String EVENT_NAME = "topClick"; + @Deprecated public ViewGroupClickEvent(int viewId) { - super(viewId); + this(-1, viewId); + } + + public ViewGroupClickEvent(int surfaceId, int viewId) { + super(surfaceId, viewId); } @Override @@ -29,8 +35,9 @@ public boolean canCoalesce() { return false; } + @Nullable @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), Arguments.createMap()); + protected WritableMap getEventData() { + return Arguments.createMap(); } } diff --git a/android/ReactAndroid/src/main/java/com/facebook/systrace/BUCK b/android/ReactAndroid/src/main/java/com/facebook/systrace/BUCK index 21006f1842c39..ef6249a999177 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/systrace/BUCK +++ b/android/ReactAndroid/src/main/java/com/facebook/systrace/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library") rn_android_library( name = "systrace", srcs = glob(["*.java"]), + autoglob = False, visibility = [ "PUBLIC", ], diff --git a/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java b/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java index 9fc8bac9bbbb0..109172eefc356 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java +++ b/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java @@ -9,7 +9,15 @@ import javax.annotation.Nullable; -public abstract class YogaNode { +public abstract class YogaNode implements YogaProps { + + /** The interface the {@link #getData()} object can optionally implement. */ + public interface Inputs { + + /** Requests the data object to disable mutations of its inputs. */ + void freeze(final YogaNode node, final @Nullable YogaNode parent); + } + public abstract void reset(); public abstract int getChildCount(); @@ -25,12 +33,10 @@ public abstract class YogaNode { public abstract YogaNode removeChildAt(int i); /** - * @returns the {@link YogaNode} that owns this {@link YogaNode}. - * The owner is used to identify the YogaTree that a {@link YogaNode} belongs - * to. - * This method will return the parent of the {@link YogaNode} when the - * {@link YogaNode} only belongs to one YogaTree or null when the - * {@link YogaNode} is shared between two or more YogaTrees. + * @returns the {@link YogaNode} that owns this {@link YogaNode}. The owner is used to identify + * the YogaTree that a {@link YogaNode} belongs to. This method will return the parent of the + * {@link YogaNode} when the {@link YogaNode} only belongs to one YogaTree or null when the + * {@link YogaNode} is shared between two or more YogaTrees. */ @Nullable public abstract YogaNode getOwner(); diff --git a/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeJNIBase.java b/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeJNIBase.java index 7ab391cce51cf..7efb8ce1dac2d 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeJNIBase.java +++ b/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeJNIBase.java @@ -83,6 +83,9 @@ public YogaNodeJNIBase getChildAt(int i) { } public void addChildAt(YogaNode c, int i) { + if (!(c instanceof YogaNodeJNIBase)) { + return; + } YogaNodeJNIBase child = (YogaNodeJNIBase) c; if (child.mOwner != null) { throw new IllegalStateException("Child already has a parent, it must be removed first."); @@ -105,6 +108,9 @@ public boolean isReferenceBaseline() { } public void swapChildAt(YogaNode newChild, int position) { + if (!(newChild instanceof YogaNodeJNIBase)) { + return; + } YogaNodeJNIBase child = (YogaNodeJNIBase) newChild; mChildren.remove(position); mChildren.add(position, child); @@ -191,12 +197,18 @@ public void calculateLayout(float width, float height) { long[] nativePointers = null; YogaNodeJNIBase[] nodes = null; + freeze(null); + ArrayList n = new ArrayList<>(); n.add(this); for (int i = 0; i < n.size(); ++i) { - List children = n.get(i).mChildren; + final YogaNodeJNIBase parent = n.get(i); + List children = parent.mChildren; if (children != null) { - n.addAll(children); + for (YogaNodeJNIBase child : children) { + child.freeze(parent); + n.add(child); + } } } @@ -209,6 +221,13 @@ public void calculateLayout(float width, float height) { YogaNative.jni_YGNodeCalculateLayoutJNI(mNativePointer, width, height, nativePointers, nodes); } + private void freeze(YogaNode parent) { + Object data = getData(); + if (data instanceof Inputs) { + ((Inputs) data).freeze(this, parent); + } + } + public void dirty() { YogaNative.jni_YGNodeMarkDirtyJNI(mNativePointer); } @@ -223,6 +242,9 @@ public boolean isDirty() { @Override public void copyStyle(YogaNode srcNode) { + if (!(srcNode instanceof YogaNodeJNIBase)) { + return; + } YogaNative.jni_YGNodeCopyStyleJNI(mNativePointer, ((YogaNodeJNIBase) srcNode).mNativePointer); } diff --git a/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaProps.java b/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaProps.java new file mode 100644 index 0000000000000..9faa520f98d97 --- /dev/null +++ b/android/ReactAndroid/src/main/java/com/facebook/yoga/YogaProps.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.yoga; + +public interface YogaProps { + + /* Width properties */ + + void setWidth(float width); + + void setWidthPercent(float percent); + + void setMinWidth(float minWidth); + + void setMinWidthPercent(float percent); + + void setMaxWidth(float maxWidth); + + void setMaxWidthPercent(float percent); + + void setWidthAuto(); + + /* Height properties */ + + void setHeight(float height); + + void setHeightPercent(float percent); + + void setMinHeight(float minHeight); + + void setMinHeightPercent(float percent); + + void setMaxHeight(float maxHeight); + + void setMaxHeightPercent(float percent); + + void setHeightAuto(); + + /* Margin properties */ + + void setMargin(YogaEdge edge, float margin); + + void setMarginPercent(YogaEdge edge, float percent); + + void setMarginAuto(YogaEdge edge); + + /* Padding properties */ + + void setPadding(YogaEdge edge, float padding); + + void setPaddingPercent(YogaEdge edge, float percent); + + /* Position properties */ + + void setPositionType(YogaPositionType positionType); + + void setPosition(YogaEdge edge, float position); + + void setPositionPercent(YogaEdge edge, float percent); + + /* Alignment properties */ + + void setAlignContent(YogaAlign alignContent); + + void setAlignItems(YogaAlign alignItems); + + void setAlignSelf(YogaAlign alignSelf); + + /* Flex properties */ + + void setFlex(float flex); + + void setFlexBasisAuto(); + + void setFlexBasisPercent(float percent); + + void setFlexBasis(float flexBasis); + + void setFlexDirection(YogaFlexDirection direction); + + void setFlexGrow(float flexGrow); + + void setFlexShrink(float flexShrink); + + /* Other properties */ + + void setJustifyContent(YogaJustify justifyContent); + + void setDirection(YogaDirection direction); + + void setBorder(YogaEdge edge, float value); + + void setWrap(YogaWrap wrap); + + void setAspectRatio(float aspectRatio); + + void setIsReferenceBaseline(boolean isReferenceBaseline); + + void setMeasureFunction(YogaMeasureFunction measureFunction); + + void setBaselineFunction(YogaBaselineFunction yogaBaselineFunction); + + /* Getters */ + + YogaValue getWidth(); + + YogaValue getMinWidth(); + + YogaValue getMaxWidth(); + + YogaValue getHeight(); + + YogaValue getMinHeight(); + + YogaValue getMaxHeight(); + + YogaDirection getStyleDirection(); + + YogaFlexDirection getFlexDirection(); + + YogaJustify getJustifyContent(); + + YogaAlign getAlignItems(); + + YogaAlign getAlignSelf(); + + YogaAlign getAlignContent(); + + YogaPositionType getPositionType(); + + float getFlexGrow(); + + float getFlexShrink(); + + YogaValue getFlexBasis(); + + float getAspectRatio(); + + YogaValue getMargin(YogaEdge edge); + + YogaValue getPadding(YogaEdge edge); + + YogaValue getPosition(YogaEdge edge); + + float getBorder(YogaEdge edge); +} diff --git a/android/ReactAndroid/src/main/jni/Application.mk b/android/ReactAndroid/src/main/jni/Application.mk index 86ed78e2bbfbb..f90f6b9418bee 100644 --- a/android/ReactAndroid/src/main/jni/Application.mk +++ b/android/ReactAndroid/src/main/jni/Application.mk @@ -5,7 +5,7 @@ APP_BUILD_SCRIPT := Android.mk -APP_ABI := $(if $(NDK_ABI_FILTERS),$(NDK_ABI_FILTERS),$(armeabi-v7a x86 arm64-v8a x86_64)) +APP_ABI := armeabi-v7a x86 arm64-v8a x86_64 APP_PLATFORM := android-21 APP_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST))) diff --git a/android/ReactAndroid/src/main/jni/first-party/fb/BUCK b/android/ReactAndroid/src/main/jni/first-party/fb/BUCK index 0acfb090124ed..5575aad44c13e 100644 --- a/android/ReactAndroid/src/main/jni/first-party/fb/BUCK +++ b/android/ReactAndroid/src/main/jni/first-party/fb/BUCK @@ -23,6 +23,10 @@ oss_cxx_library( "-DDISABLE_CPUCAP", "-DDISABLE_XPLAT", ], + linker_flags = [ + "-llog", + ], + preferred_linkage = "static", soname = "libfb.$(ext)", visibility = ["PUBLIC"], deps = [ diff --git a/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/RefPtr.h b/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/RefPtr.h index 1384b6534a5db..6b24554ede634 100644 --- a/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/RefPtr.h +++ b/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/RefPtr.h @@ -204,7 +204,7 @@ static inline RefPtr adoptRef(T *ptr) { } template -static inline RefPtr createNew(Args &&... arguments) { +static inline RefPtr createNew(Args &&...arguments) { return RefPtr::adoptRef(new T(std::forward(arguments)...)); } diff --git a/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/StaticInitialized.h b/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/StaticInitialized.h index 839400f13a278..d5c553423aa4c 100644 --- a/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/StaticInitialized.h +++ b/android/ReactAndroid/src/main/jni/first-party/fb/include/fb/StaticInitialized.h @@ -21,7 +21,7 @@ class StaticInitialized { constexpr StaticInitialized() : m_instance(nullptr) {} template - void initialize(Args &&... arguments) { + void initialize(Args &&...arguments) { FBASSERT(!m_instance); m_instance = new T(std::forward(arguments)...); } diff --git a/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp b/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp index 6534382116eb3..71fe98f3e75e8 100644 --- a/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp +++ b/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp @@ -367,11 +367,7 @@ static void jni_YGNodeCalculateLayoutJNI( void* layoutContext = nullptr; auto map = PtrJNodeMapVanilla{}; if (nativePointers) { - size_t nativePointersSize = env->GetArrayLength(nativePointers); - jlong result[nativePointersSize]; - env->GetLongArrayRegion(nativePointers, 0, nativePointersSize, result); - - map = PtrJNodeMapVanilla{result, nativePointersSize, javaNodes}; + map = PtrJNodeMapVanilla{nativePointers, javaNodes}; layoutContext = ↦ } @@ -726,8 +722,7 @@ static void jni_YGNodePrintJNI(JNIEnv* env, jobject obj, jlong nativePointer) { const YGNodeRef node = _jlong2YGNodeRef(nativePointer); YGNodePrint( node, - (YGPrintOptions)( - YGPrintOptionsStyle | YGPrintOptionsLayout | YGPrintOptionsChildren)); + (YGPrintOptions) (YGPrintOptionsStyle | YGPrintOptionsLayout | YGPrintOptionsChildren)); #endif } diff --git a/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJTypesVanilla.h b/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJTypesVanilla.h index 8f8c778609ba9..d1202b755fa7b 100644 --- a/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJTypesVanilla.h +++ b/android/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJTypesVanilla.h @@ -20,11 +20,15 @@ class PtrJNodeMapVanilla { public: PtrJNodeMapVanilla() : ptrsToIdxs_{}, javaNodes_{} {} - PtrJNodeMapVanilla( - jlong* nativePointers, - size_t nativePointersSize, - jobjectArray javaNodes) + PtrJNodeMapVanilla(jlongArray javaNativePointers, jobjectArray javaNodes) : javaNodes_{javaNodes} { + + JNIEnv* env = getCurrentEnv(); + size_t nativePointersSize = env->GetArrayLength(javaNativePointers); + std::vector nativePointers(nativePointersSize); + env->GetLongArrayRegion( + javaNativePointers, 0, nativePointersSize, nativePointers.data()); + for (size_t i = 0; i < nativePointersSize; ++i) { ptrsToIdxs_[(YGNodeRef) nativePointers[i]] = i; } diff --git a/android/ReactAndroid/src/main/jni/react/jni/Android.mk b/android/ReactAndroid/src/main/jni/react/jni/Android.mk index 76c613b0f466e..af2077b5a6631 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/android/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -27,7 +27,7 @@ LOCAL_CFLAGS += -fexceptions -frtti -Wno-unused-lambda-capture LOCAL_LDLIBS += -landroid # The dynamic libraries (.so files) that this module depends on. -LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libglog_init libyoga +LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libglog_init libyoga libreact_render_runtimescheduler # The static libraries (.a files) that this module depends on. LOCAL_STATIC_LIBRARIES := libreactnative libcallinvokerholder libruntimeexecutor @@ -39,7 +39,7 @@ LOCAL_STATIC_LIBRARIES := libreactnative libcallinvokerholder libruntimeexecutor LOCAL_MODULE := reactnativeutilsjni # Compile all local c++ files. -LOCAL_SRC_FILES := $(wildcard Cxx*.cpp) $(wildcard J*.cpp) $(wildcard M*.cpp) $(wildcard N*.cpp) $(wildcard P*.cpp) $(wildcard R*.cpp) $(wildcard W*.cpp) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) ifeq ($(APP_OPTIM),debug) # Keep symbols by overriding the strip command invoked by ndk-build. @@ -77,7 +77,7 @@ LOCAL_CFLAGS += -fexceptions -frtti -Wno-unused-lambda-capture LOCAL_LDLIBS += -landroid # The dynamic libraries (.so files) that this module depends on. -LOCAL_SHARED_LIBRARIES := libreactnativeutilsjni libfolly_json libfb libfbjni libglog_init libyoga +LOCAL_SHARED_LIBRARIES := libreactnativeutilsjni libfolly_json libfb libfbjni libglog_init libyoga logger libreact_render_runtimescheduler # The static libraries (.a files) that this module depends on. LOCAL_STATIC_LIBRARIES := libreactnative libruntimeexecutor libcallinvokerholder @@ -124,21 +124,20 @@ $(call import-module,yogajni) $(call import-module,cxxreact) $(call import-module,jsi) $(call import-module,jsiexecutor) +$(call import-module,logger) $(call import-module,callinvoker) $(call import-module,reactperflogger) $(call import-module,hermes) $(call import-module,runtimeexecutor) +$(call import-module,react/renderer/runtimescheduler) $(call import-module,react/nativemodule/core) include $(REACT_SRC_DIR)/reactperflogger/jni/Android.mk # TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..." # Note: Update this only when ready to minimize breaking changes. include $(REACT_SRC_DIR)/turbomodule/core/jni/Android.mk - -ifeq ($(BUILD_FABRIC),true) - include $(REACT_SRC_DIR)/viewmanagers/jni/Android.mk - include $(REACT_SRC_DIR)/fabric/jni/Android.mk -endif +include $(REACT_SRC_DIR)/fabric/jni/Android.mk +include $(REACT_SRC_DIR)/common/mapbuffer/jni/Android.mk # TODO(ramanpreet): # Why doesn't this import-module call generate a jscexecutor.so file? diff --git a/android/ReactAndroid/src/main/jni/react/jni/BUCK b/android/ReactAndroid/src/main/jni/react/jni/BUCK index de79bff729e9c..c2a91196bb29b 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/BUCK +++ b/android/ReactAndroid/src/main/jni/react/jni/BUCK @@ -20,6 +20,7 @@ EXPORTED_HEADERS = [ "ReadableNativeArray.h", "ReadableNativeMap.h", "JRuntimeExecutor.h", + "JRuntimeScheduler.h", "WritableNativeArray.h", "WritableNativeMap.h", ] @@ -34,14 +35,11 @@ rn_xplat_cxx_library( header_namespace = "react/jni", exported_headers = EXPORTED_HEADERS, compiler_flags = [ - "-Wall", - "-Werror", - "-fexceptions", - "-std=c++1y", - "-frtti", "-Wno-pessimizing-move", "-Wno-inconsistent-missing-override", ], + compiler_flags_enable_exceptions = True, + compiler_flags_enable_rtti = True, # dynamic_cast used within ReadableNative* fbandroid_allow_jni_merging = True, labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, @@ -54,7 +52,7 @@ rn_xplat_cxx_library( visibility = [ "PUBLIC", ], - deps = ([ + deps = [ "//xplat/third-party/linker_lib:android", "//xplat/third-party/linker_lib:atomic", "//third-party/glog:glog", @@ -67,7 +65,9 @@ rn_xplat_cxx_library( react_native_xplat_target("cxxreact:module"), react_native_xplat_target("jsinspector:jsinspector"), react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + react_native_xplat_target("react/renderer/runtimescheduler:runtimescheduler"), + react_native_xplat_target("logger:logger"), react_native_xplat_dep("jsi:jsi"), FBJNI_TARGET, - ]) if not IS_OSS_BUILD else [], + ] if not IS_OSS_BUILD else [], ) diff --git a/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp b/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp index 3d2f3141a24a9..1c4d2015eb967 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp +++ b/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp @@ -26,9 +26,17 @@ #include #include #include +#include +#include +#include +#include + +#include #include "CxxModuleWrapper.h" #include "JNativeRunnable.h" +#include "JReactCxxErrorHandler.h" +#include "JReactSoftExceptionLogger.h" #include "JavaScriptExecutorHolder.h" #include "JniJSModulesUnbundle.h" #include "NativeArray.h" @@ -85,12 +93,25 @@ class JInstanceCallback : public InstanceCallback { } // namespace jni::local_ref -CatalystInstanceImpl::initHybrid(jni::alias_ref) { - return makeCxxInstance(); +CatalystInstanceImpl::initHybrid( + jni::alias_ref, + bool enableRuntimeScheduler, + bool enableRuntimeSchedulerInTurboModule) { + return makeCxxInstance( + enableRuntimeScheduler, enableRuntimeSchedulerInTurboModule); } -CatalystInstanceImpl::CatalystInstanceImpl() - : instance_(std::make_unique()) {} +CatalystInstanceImpl::CatalystInstanceImpl( + bool enableRuntimeScheduler, + bool enableRuntimeSchedulerInTurboModule) + : instance_(std::make_unique()), + enableRuntimeScheduler_(enableRuntimeScheduler), + enableRuntimeSchedulerInTurboModule_( + enableRuntimeScheduler && enableRuntimeSchedulerInTurboModule) {} + +void CatalystInstanceImpl::warnOnLegacyNativeModuleSystemUse() { + CxxNativeModule::setShouldWarnOnUse(true); +} void CatalystInstanceImpl::registerNatives() { registerHybrid({ @@ -129,11 +150,36 @@ void CatalystInstanceImpl::registerNatives() { CatalystInstanceImpl::handleMemoryPressure), makeNativeMethod( "getRuntimeExecutor", CatalystInstanceImpl::getRuntimeExecutor), + makeNativeMethod( + "getRuntimeScheduler", CatalystInstanceImpl::getRuntimeScheduler), + makeNativeMethod( + "warnOnLegacyNativeModuleSystemUse", + CatalystInstanceImpl::warnOnLegacyNativeModuleSystemUse), }); JNativeRunnable::registerNatives(); } +void log(ReactNativeLogLevel level, const char *message) { + switch (level) { + case ReactNativeLogLevelInfo: + LOG(INFO) << message; + break; + case ReactNativeLogLevelWarning: + LOG(WARNING) << message; + JReactSoftExceptionLogger::logNoThrowSoftExceptionWithMessage( + "react_native_log#warning", message); + break; + case ReactNativeLogLevelError: + LOG(ERROR) << message; + JReactCxxErrorHandler::handleError(message); + break; + case ReactNativeLogLevelFatal: + LOG(FATAL) << message; + break; + } +} + void CatalystInstanceImpl::initializeBridge( jni::alias_ref callback, // This executor is actually a factory holder. @@ -144,6 +190,8 @@ void CatalystInstanceImpl::initializeBridge( javaModules, jni::alias_ref::javaobject> cxxModules) { + set_react_native_logfunc(&log); + // TODO mhorowitz: how to assert here? // Assertions.assertCondition(mBridge == null, "initializeBridge should be // called once"); @@ -229,7 +277,30 @@ void CatalystInstanceImpl::jniLoadScriptFromFile( const std::string &fileName, const std::string &sourceURL, bool loadSynchronously) { - if (Instance::isIndexedRAMBundle(fileName.c_str())) { + auto reactInstance = instance_; + if (reactInstance && Instance::isHBCBundle(fileName.c_str())) { + std::unique_ptr script; + RecoverableError::runRethrowingAsRecoverable( + [&fileName, &script]() { + script = JSBigFileString::fromPath(fileName); + }); + const char *buffer = script->c_str(); + uint32_t bufferLength = (uint32_t)script->size(); + uint32_t offset = 8; + while (offset < bufferLength) { + uint32_t segment = offset + 4; + uint32_t moduleLength = + bufferLength < segment ? 0 : *(((uint32_t *)buffer) + offset / 4); + + reactInstance->loadScriptFromString( + std::make_unique( + std::string(buffer + segment, buffer + moduleLength + segment)), + sourceURL, + false); + + offset += ((moduleLength + 3) & ~3) + 4; + } + } else if (Instance::isIndexedRAMBundle(fileName.c_str())) { instance_->loadRAMBundleFromFile(fileName, sourceURL, loadSynchronously); } else { std::unique_ptr script; @@ -293,10 +364,18 @@ void CatalystInstanceImpl::handleMemoryPressure(int pressureLevel) { jni::alias_ref CatalystInstanceImpl::getJSCallInvokerHolder() { if (!jsCallInvokerHolder_) { - jsCallInvokerHolder_ = jni::make_global( - CallInvokerHolder::newObjectCxxArgs(instance_->getJSCallInvoker())); + if (enableRuntimeSchedulerInTurboModule_) { + auto runtimeScheduler = getRuntimeScheduler(); + auto runtimeSchedulerCallInvoker = + std::make_shared( + runtimeScheduler->cthis()->get()); + jsCallInvokerHolder_ = jni::make_global( + CallInvokerHolder::newObjectCxxArgs(runtimeSchedulerCallInvoker)); + } else { + jsCallInvokerHolder_ = jni::make_global( + CallInvokerHolder::newObjectCxxArgs(instance_->getJSCallInvoker())); + } } - return jsCallInvokerHolder_; } @@ -341,5 +420,23 @@ CatalystInstanceImpl::getRuntimeExecutor() { return runtimeExecutor_; } +jni::alias_ref +CatalystInstanceImpl::getRuntimeScheduler() { + if (enableRuntimeScheduler_ && !runtimeScheduler_) { + auto runtimeExecutor = instance_->getRuntimeExecutor(); + auto runtimeScheduler = std::make_shared(runtimeExecutor); + + runtimeScheduler_ = + jni::make_global(JRuntimeScheduler::newObjectCxxArgs(runtimeScheduler)); + + runtimeExecutor([runtimeScheduler](jsi::Runtime &runtime) { + RuntimeSchedulerBinding::createAndInstallIfNeeded( + runtime, runtimeScheduler); + }); + } + + return runtimeScheduler_; +} + } // namespace react } // namespace facebook diff --git a/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h b/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h index 5fc320484ade9..21e9b592dc302 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h +++ b/android/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h @@ -15,6 +15,7 @@ #include "CxxModuleWrapper.h" #include "JMessageQueueThread.h" #include "JRuntimeExecutor.h" +#include "JRuntimeScheduler.h" #include "JSLoader.h" #include "JavaModuleWrapper.h" #include "ModuleRegistryBuilder.h" @@ -36,7 +37,10 @@ class CatalystInstanceImpl : public jni::HybridClass { static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/CatalystInstanceImpl;"; - static jni::local_ref initHybrid(jni::alias_ref); + static jni::local_ref initHybrid( + jni::alias_ref, + bool enableRuntimeScheduler, + bool enableRuntimeSchedulerInTurboModule); static void registerNatives(); @@ -47,7 +51,9 @@ class CatalystInstanceImpl : public jni::HybridClass { private: friend HybridBase; - CatalystInstanceImpl(); + CatalystInstanceImpl( + bool enableRuntimeScheduler, + bool enableRuntimeSchedulerInTurboModule); void initializeBridge( jni::alias_ref callback, @@ -61,6 +67,10 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref::javaobject> cxxModules); + // When called from CatalystInstanceImpl.java, warnings will be logged when + // CxxNativeModules are used. Java NativeModule usages log error in Java. + void warnOnLegacyNativeModuleSystemUse(); + void extendNativeModules( jni::alias_ref::javaobject> javaModules, @@ -98,10 +108,13 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::alias_ref getJSCallInvokerHolder(); jni::alias_ref getNativeCallInvokerHolder(); jni::alias_ref getRuntimeExecutor(); + jni::alias_ref getRuntimeScheduler(); void setGlobalVariable(std::string propName, std::string &&jsonValue); jlong getJavaScriptContext(); void handleMemoryPressure(int pressureLevel); + void createAndInstallRuntimeSchedulerIfNecessary(); + // This should be the only long-lived strong reference, but every C++ class // will have a weak reference. std::shared_ptr instance_; @@ -110,6 +123,10 @@ class CatalystInstanceImpl : public jni::HybridClass { jni::global_ref jsCallInvokerHolder_; jni::global_ref nativeCallInvokerHolder_; jni::global_ref runtimeExecutor_; + jni::global_ref runtimeScheduler_; + + bool const enableRuntimeScheduler_; + bool const enableRuntimeSchedulerInTurboModule_; }; } // namespace react diff --git a/android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.cpp b/android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.cpp new file mode 100644 index 0000000000000..5b3409eb1b47b --- /dev/null +++ b/android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "JReactCxxErrorHandler.h" + +using namespace facebook::react; + +void JReactCxxErrorHandler::handleError(std::string message) { + static const auto handleError = + javaClassStatic()->getStaticMethod( + "handleError"); + + return handleError(javaClassStatic(), message); +} diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/primitives.h b/android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.h similarity index 51% rename from android/ReactCommon/react/renderer/components/picker/iospicker/primitives.h rename to android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.h index d9804aa98868e..0ed379b9f139d 100644 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/primitives.h +++ b/android/ReactAndroid/src/main/jni/react/jni/JReactCxxErrorHandler.h @@ -7,22 +7,18 @@ #pragma once -#include - +#include #include namespace facebook { namespace react { -struct PickerItemsStruct { - std::string label; - std::string value; - SharedColor textColor; +class JReactCxxErrorHandler : public jni::JavaClass { + public: + static constexpr const char *kJavaDescriptor = + "Lcom/facebook/react/bridge/ReactCxxErrorHandler;"; - bool operator==(const PickerItemsStruct &rhs) const { - return ( - label == rhs.label && value == rhs.value && textColor == rhs.textColor); - } + static void handleError(std::string message); }; } // namespace react diff --git a/android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.cpp b/android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.cpp new file mode 100644 index 0000000000000..788de6f99abcd --- /dev/null +++ b/android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "JReactSoftExceptionLogger.h" + +using namespace facebook::react; + +void JReactSoftExceptionLogger::logNoThrowSoftExceptionWithMessage( + std::string tag, + std::string message) { + static const auto logNoThrowSoftExceptionWithMessage = + javaClassStatic() + ->getStaticMethod( + "logNoThrowSoftExceptionWithMessage"); + + return logNoThrowSoftExceptionWithMessage(javaClassStatic(), tag, message); +} diff --git a/android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.h b/android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.h new file mode 100644 index 0000000000000..6a91ec709af9b --- /dev/null +++ b/android/ReactAndroid/src/main/jni/react/jni/JReactSoftExceptionLogger.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +class JReactSoftExceptionLogger + : public jni::JavaClass { + public: + static constexpr const char *kJavaDescriptor = + "Lcom/facebook/react/bridge/ReactSoftExceptionLogger;"; + + static void logNoThrowSoftExceptionWithMessage( + std::string tag, + std::string message); +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerComponentDescriptor.h b/android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.cpp similarity index 51% rename from android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerComponentDescriptor.h rename to android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.cpp index cbad671add261..1176d21f31c1d 100644 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerComponentDescriptor.h +++ b/android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.cpp @@ -5,17 +5,18 @@ * LICENSE file in the root directory of this source tree. */ -#pragma once - -#include "AndroidDropdownPickerShadowNode.h" - -#include +#include "JRuntimeScheduler.h" namespace facebook { namespace react { -using AndroidDropdownPickerComponentDescriptor = - ConcreteComponentDescriptor; +JRuntimeScheduler::JRuntimeScheduler( + std::weak_ptr runtimeScheduler) + : runtimeScheduler_(runtimeScheduler) {} + +std::weak_ptr JRuntimeScheduler::get() { + return runtimeScheduler_; +} } // namespace react } // namespace facebook diff --git a/android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.h b/android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.h new file mode 100644 index 0000000000000..3f3f7e857d6a5 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/react/jni/JRuntimeScheduler.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +class JRuntimeScheduler : public jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = + "Lcom/facebook/react/bridge/RuntimeScheduler;"; + + std::weak_ptr get(); + + private: + friend HybridBase; + JRuntimeScheduler(std::weak_ptr runtimeScheduler); + std::weak_ptr runtimeScheduler_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp b/android/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp index 2b07dcb7530b3..c0833d94ac818 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp +++ b/android/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp @@ -66,7 +66,7 @@ loadScriptFromAssets(AAssetManager *manager, const std::string &assetName) { throw std::runtime_error(folly::to( "Unable to load script. Make sure you're " - "either running Metro (run 'react-native start') or that your bundle '", + "either running Metro (run 'npx react-native start') or that your bundle '", assetName, "' is packaged correctly for release.")); } diff --git a/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp b/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp index e8281b8f1cbed..14aa1b54a57c6 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp +++ b/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp @@ -152,100 +152,6 @@ MethodCallResult JavaNativeModule::callSerializableNativeHook( return method->invoke(instance_, wrapper_->getModule(), params); } -NewJavaNativeModule::NewJavaNativeModule( - std::weak_ptr instance, - jni::alias_ref wrapper, - std::shared_ptr messageQueueThread) - : instance_(std::move(instance)), - wrapper_(make_global(wrapper)), - module_(make_global(wrapper->getModule())), - messageQueueThread_(std::move(messageQueueThread)) { - auto descs = wrapper_->getMethodDescriptors(); - std::string moduleName = getName(); - methods_.reserve(descs->size()); - - for (const auto &desc : *descs) { - auto type = desc->getType(); - auto name = desc->getName(); - methods_.emplace_back( - desc->getMethod(), - desc->getName(), - desc->getSignature(), - moduleName + "." + name, - type == "syncHook"); - - methodDescriptors_.emplace_back(name, type); - } -} - -std::string NewJavaNativeModule::getName() { - static auto getNameMethod = - wrapper_->getClass()->getMethod("getName"); - return getNameMethod(wrapper_)->toStdString(); -} - -std::vector NewJavaNativeModule::getMethods() { - return methodDescriptors_; -} - -folly::dynamic NewJavaNativeModule::getConstants() { - static auto constantsMethod = - wrapper_->getClass()->getMethod("getConstants"); - auto constants = constantsMethod(wrapper_); - if (!constants) { - return nullptr; - } else { - return cthis(constants)->consume(); - } -} - -void NewJavaNativeModule::invoke( - unsigned int reactMethodId, - folly::dynamic &¶ms, - int callId) { - if (reactMethodId >= methods_.size()) { - throw std::invalid_argument(folly::to( - "methodId ", - reactMethodId, - " out of range [0..", - methods_.size(), - "]")); - } - CHECK(!methods_[reactMethodId].isSyncHook()) - << "Trying to invoke a synchronous hook asynchronously"; - messageQueueThread_->runOnQueue( - [this, reactMethodId, params = std::move(params), callId]() mutable { -#ifdef WITH_FBSYSTRACE - if (callId != -1) { - fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); - } -#endif - invokeInner(reactMethodId, std::move(params)); - }); -} - -MethodCallResult NewJavaNativeModule::callSerializableNativeHook( - unsigned int reactMethodId, - folly::dynamic &¶ms) { - if (reactMethodId >= methods_.size()) { - throw std::invalid_argument(folly::to( - "methodId ", - reactMethodId, - " out of range [0..", - methods_.size(), - "]")); - } - CHECK(methods_[reactMethodId].isSyncHook()) - << "Trying to invoke a asynchronous method as synchronous hook"; - return invokeInner(reactMethodId, std::move(params)); -} - -MethodCallResult NewJavaNativeModule::invokeInner( - unsigned int reactMethodId, - folly::dynamic &¶ms) { - return methods_[reactMethodId].invoke(instance_, module_.get(), params); -} - jni::local_ref JMethodDescriptor::getMethod() const { static auto method = diff --git a/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h b/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h index f4b46ce66b4ff..414ee916e863b 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h +++ b/android/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h @@ -85,35 +85,5 @@ class JavaNativeModule : public NativeModule { std::vector> syncMethods_; }; -// Experimental new implementation that uses direct method invocation -class NewJavaNativeModule : public NativeModule { - public: - NewJavaNativeModule( - std::weak_ptr instance, - jni::alias_ref wrapper, - std::shared_ptr messageQueueThread); - - std::string getName() override; - std::vector getMethods() override; - folly::dynamic getConstants() override; - void invoke(unsigned int reactMethodId, folly::dynamic &¶ms, int callId) - override; - MethodCallResult callSerializableNativeHook( - unsigned int reactMethodId, - folly::dynamic &¶ms) override; - - private: - std::weak_ptr instance_; - jni::global_ref wrapper_; - jni::global_ref module_; - std::shared_ptr messageQueueThread_; - std::vector methods_; - std::vector methodDescriptors_; - - MethodCallResult invokeInner( - unsigned int reactMethodId, - folly::dynamic &¶ms); -}; - } // namespace react } // namespace facebook diff --git a/android/ReactAndroid/src/main/jni/react/jni/ProxyExecutor.h b/android/ReactAndroid/src/main/jni/react/jni/ProxyExecutor.h index c9eab4170d41f..d4adbf6d7219d 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/ProxyExecutor.h +++ b/android/ReactAndroid/src/main/jni/react/jni/ProxyExecutor.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include "OnLoad.h" diff --git a/android/ReactAndroid/src/main/jni/react/jni/ReadableNativeMap.cpp b/android/ReactAndroid/src/main/jni/react/jni/ReadableNativeMap.cpp index c1ad15082bebf..61f686c440446 100644 --- a/android/ReactAndroid/src/main/jni/react/jni/ReadableNativeMap.cpp +++ b/android/ReactAndroid/src/main/jni/react/jni/ReadableNativeMap.cpp @@ -64,14 +64,15 @@ local_ref> ReadableNativeMap::importKeys() { return JArrayClass::newArray(0); } auto pairs = map_.items(); - for (auto &pair : pairs) { - keys_.value().push_back(pair.first.asString()); - } - jint size = keys_.value().size(); + jint size = map_.size(); auto jarray = JArrayClass::newArray(size); - for (jint ii = 0; ii < size; ii++) { - (*jarray)[ii] = make_jstring(keys_.value()[ii].getString()); + jint i = 0; + for (auto &pair : pairs) { + auto value = pair.first.asString(); + keys_.value().push_back(value); + (*jarray)[i++] = make_jstring(value); } + return jarray; } diff --git a/android/ReactAndroid/src/main/jni/react/perftests/BUCK b/android/ReactAndroid/src/main/jni/react/perftests/BUCK index fbff1d6525055..3675a6157b289 100644 --- a/android/ReactAndroid/src/main/jni/react/perftests/BUCK +++ b/android/ReactAndroid/src/main/jni/react/perftests/BUCK @@ -3,10 +3,6 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "react_native_xplat_target rn_xplat_cxx_library( name = "perftests", srcs = ["OnLoad.cpp"], - compiler_flags = [ - "-fexceptions", - "-std=c++1y", - ], platforms = ANDROID, soname = "libnativereactperftests.$(ext)", visibility = [ diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/Android.mk b/android/ReactAndroid/src/main/jni/third-party/boost/Android.mk index 089e7d84b5a47..6092d28c5d932 100644 --- a/android/ReactAndroid/src/main/jni/third-party/boost/Android.mk +++ b/android/ReactAndroid/src/main/jni/third-party/boost/Android.mk @@ -1,9 +1,14 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +# These ASM files are picked from the boost release separately, +# because the react native version does not include anything outside of headers. +# They are required for Folly futures to compile successfully. +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/asm/$(TARGET_ARCH)/*.S) + LOCAL_C_INCLUDES := $(LOCAL_PATH)/boost_1_63_0 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/boost_1_63_0 -LOCAL_MODULE := boost +LOCAL_MODULE := boost include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/jump_arm_aapcs_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/jump_arm_aapcs_elf_gas.S new file mode 100644 index 0000000000000..d0f7fa24c6a92 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/jump_arm_aapcs_elf_gas.S @@ -0,0 +1,86 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,%function +jump_fcontext: + @ save LR as PC + push {lr} + @ save hidden,V1-V8,LR + push {a1,v1-v8,lr} + + @ prepare stack for FPU + sub sp, sp, #64 +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ save S16-S31 + vstmia sp, {d8-d15} +#endif + + @ store RSP (pointing to context-data) in A1 + mov a1, sp + + @ restore RSP (pointing to context-data) from A2 + mov sp, a2 + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ restore S16-S31 + vldmia sp, {d8-d15} +#endif + @ prepare stack for FPU + add sp, sp, #64 + + @ restore hidden,V1-V8,LR + pop {a4,v1-v8,lr} + + @ return transfer_t from jump + str a1, [a4, #0] + str a3, [a4, #4] + @ pass transfer_t as first arg in context function + @ A1 == FCTX, A2 == DATA + mov a2, a3 + + @ restore PC + pop {pc} +.size jump_fcontext,.-jump_fcontext + +@ Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/make_arm_aapcs_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/make_arm_aapcs_elf_gas.S new file mode 100644 index 0000000000000..993dac1092224 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/make_arm_aapcs_elf_gas.S @@ -0,0 +1,79 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl make_fcontext +.align 2 +.type make_fcontext,%function +make_fcontext: + @ shift address in A1 to lower 16 byte boundary + bic a1, a1, #15 + + @ reserve space for context-data on context-stack + sub a1, a1, #128 + + @ third arg of make_fcontext() == address of context-function + str a3, [a1, #104] + + @ compute address of returned transfer_t + add a2, a1, #108 + mov a3, a2 + str a3, [a1, #64] + + @ compute abs address of label finish + adr a2, finish + @ save address of finish as return-address for context-function + @ will be entered after context-function returns + str a2, [a1, #100] + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) +#endif + + bx lr @ return pointer to context-data + +finish: + @ exit code is zero + mov a1, #0 + @ exit application + bl _exit@PLT +.size make_fcontext,.-make_fcontext + +@ Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/ontop_arm_aapcs_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/ontop_arm_aapcs_elf_gas.S new file mode 100644 index 0000000000000..9d9198fc552a5 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm/ontop_arm_aapcs_elf_gas.S @@ -0,0 +1,91 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | v8 | lr | pc | FCTX| DATA| | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.text +.globl ontop_fcontext +.align 2 +.type ontop_fcontext,%function +ontop_fcontext: + @ save LR as PC + push {lr} + @ save hidden,V1-V8,LR + push {a1,v1-v8,lr} + + @ prepare stack for FPU + sub sp, sp, #64 +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ save S16-S31 + vstmia sp, {d8-d15} +#endif + + @ store RSP (pointing to context-data) in A1 + mov a1, sp + + @ restore RSP (pointing to context-data) from A2 + mov sp, a2 + + @ store parent context in A2 + mov a2, a1 + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) + @ restore S16-S31 + vldmia sp, {d8-d15} +#endif + @ prepare stack for FPU + add sp, sp, #64 + + @ restore hidden,V1-V8,LR + pop {a1,v1-v8,lr} + + @ return transfer_t from jump + str a2, [a1, #0] + str a3, [a1, #4] + @ pass transfer_t as first arg in context function + @ A1 == hidden, A2 == FCTX, A3 == DATA + + @ skip PC + add sp, sp, #4 + + @ jump to ontop-function + bx a4 +.size ontop_fcontext,.-ontop_fcontext + +@ Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/jump_arm64_aapcs_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/jump_arm64_aapcs_elf_gas.S new file mode 100644 index 0000000000000..7c0c2fa2f20bf --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/jump_arm64_aapcs_elf_gas.S @@ -0,0 +1,114 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.cpu generic+fp+simd +.text +.align 2 +.global jump_fcontext +.type jump_fcontext, %function +jump_fcontext: + # prepare stack for GP + FPU + sub sp, sp, #0xb0 + + # save d8 - d15 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + + # save x19-x30 + stp x19, x20, [sp, #0x40] + stp x21, x22, [sp, #0x50] + stp x23, x24, [sp, #0x60] + stp x25, x26, [sp, #0x70] + stp x27, x28, [sp, #0x80] + stp x29, x30, [sp, #0x90] + + # save LR as PC + str x30, [sp, #0xa0] + + # store RSP (pointing to context-data) in X0 + mov x4, sp + + # restore RSP (pointing to context-data) from X1 + mov sp, x0 + + # load d8 - d15 + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + # load x19-x30 + ldp x19, x20, [sp, #0x40] + ldp x21, x22, [sp, #0x50] + ldp x23, x24, [sp, #0x60] + ldp x25, x26, [sp, #0x70] + ldp x27, x28, [sp, #0x80] + ldp x29, x30, [sp, #0x90] + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 + + # load pc + ldr x4, [sp, #0xa0] + + # restore stack from GP + FPU + add sp, sp, #0xb0 + + ret x4 +.size jump_fcontext,.-jump_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/make_arm64_aapcs_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/make_arm64_aapcs_elf_gas.S new file mode 100644 index 0000000000000..e71a91cf6f08b --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/make_arm64_aapcs_elf_gas.S @@ -0,0 +1,85 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.cpu generic+fp+simd +.text +.align 2 +.global make_fcontext +.type make_fcontext, %function +make_fcontext: + # shift address in x0 (allocated stack) to lower 16 byte boundary + and x0, x0, ~0xF + + # reserve space for context-data on context-stack + sub x0, x0, #0xb0 + + # third arg of make_fcontext() == address of context-function + # store address as a PC to jump in + str x2, [x0, #0xa0] + + # save address of finish as return-address for context-function + # will be entered after context-function returns (LR register) + adr x1, finish + str x1, [x0, #0x98] + + ret x30 // return pointer to context-data (x0) + +finish: + # exit code is zero + mov x0, #0 + # exit application + bl _exit + +.size make_fcontext,.-make_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/ontop_arm64_aapcs_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/ontop_arm64_aapcs_elf_gas.S new file mode 100644 index 0000000000000..7e3b047a22c44 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/arm64/ontop_arm64_aapcs_elf_gas.S @@ -0,0 +1,113 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | d8 | d9 | d10 | d11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | d12 | d13 | d14 | d15 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | x19 | x20 | x21 | x22 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | x23 | x24 | x25 | x26 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | x27 | x28 | FP | LR | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | | | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| | | * + * ------------------------------------------------- * + * | PC | align | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.cpu generic+fp+simd +.text +.align 2 +.global ontop_fcontext +.type ontop_fcontext, %function +ontop_fcontext: + # prepare stack for GP + FPU + sub sp, sp, #0xb0 + + # save d8 - d15 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + + # save x19-x30 + stp x19, x20, [sp, #0x40] + stp x21, x22, [sp, #0x50] + stp x23, x24, [sp, #0x60] + stp x25, x26, [sp, #0x70] + stp x27, x28, [sp, #0x80] + stp x29, x30, [sp, #0x90] + + # save LR as PC + str x30, [sp, #0xa0] + + # store RSP (pointing to context-data) in X5 + mov x4, sp + + # restore RSP (pointing to context-data) from X1 + mov sp, x0 + + # load d8 - d15 + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + # load x19-x30 + ldp x19, x20, [sp, #0x40] + ldp x21, x22, [sp, #0x50] + ldp x23, x24, [sp, #0x60] + ldp x25, x26, [sp, #0x70] + ldp x27, x28, [sp, #0x80] + ldp x29, x30, [sp, #0x90] + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 + + # skip pc + # restore stack from GP + FPU + add sp, sp, #0xb0 + + # jump to ontop-function + ret x2 +.size ontop_fcontext,.-ontop_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/jump_i386_sysv_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/jump_i386_sysv_elf_gas.S new file mode 100644 index 0000000000000..25f01db27d0e0 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/jump_i386_sysv_elf_gas.S @@ -0,0 +1,78 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * ---------------------------------------------------------------------------------- * + * | to | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl jump_fcontext +.align 2 +.type jump_fcontext,@function +jump_fcontext: + leal -0x18(%esp), %esp /* prepare stack */ + + stmxcsr (%esp) /* save MMX control- and status-word */ + fnstcw 0x4(%esp) /* save x87 control-word */ + + movl %edi, 0x8(%esp) /* save EDI */ + movl %esi, 0xc(%esp) /* save ESI */ + movl %ebx, 0x10(%esp) /* save EBX */ + movl %ebp, 0x14(%esp) /* save EBP */ + + /* store ESP (pointing to context-data) in ECX */ + movl %esp, %ecx + + /* first arg of jump_fcontext() == fcontext to jump to */ + movl 0x20(%esp), %eax + + /* second arg of jump_fcontext() == data to be transferred */ + movl 0x24(%esp), %edx + + /* restore ESP (pointing to context-data) from EAX */ + movl %eax, %esp + + /* address of returned transport_t */ + movl 0x1c(%esp), %eax + /* return parent fcontext_t */ + movl %ecx, (%eax) + /* return data */ + movl %edx, 0x4(%eax) + + movl 0x18(%esp), %ecx /* restore EIP */ + + ldmxcsr (%esp) /* restore MMX control- and status-word */ + fldcw 0x4(%esp) /* restore x87 control-word */ + + movl 0x8(%esp), %edi /* restore EDI */ + movl 0xc(%esp), %esi /* restore ESI */ + movl 0x10(%esp), %ebx /* restore EBX */ + movl 0x14(%esp), %ebp /* restore EBP */ + + leal 0x20(%esp), %esp /* prepare stack */ + + /* jump to context */ + jmp *%ecx +.size jump_fcontext,.-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/make_i386_sysv_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/make_i386_sysv_elf_gas.S new file mode 100644 index 0000000000000..de77e8834daa7 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/make_i386_sysv_elf_gas.S @@ -0,0 +1,106 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * ---------------------------------------------------------------------------------- * + * | to | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl make_fcontext +.align 2 +.type make_fcontext,@function +make_fcontext: + /* first arg of make_fcontext() == top of context-stack */ + movl 0x4(%esp), %eax + + /* reserve space for first argument of context-function + eax might already point to a 16byte border */ + leal -0x8(%eax), %eax + + /* shift address in EAX to lower 16 byte boundary */ + andl $-16, %eax + + /* reserve space for context-data on context-stack */ + leal -0x28(%eax), %eax + + /* third arg of make_fcontext() == address of context-function */ + /* stored in EBX */ + movl 0xc(%esp), %ecx + movl %ecx, 0x10(%eax) + + /* save MMX control- and status-word */ + stmxcsr (%eax) + /* save x87 control-word */ + fnstcw 0x4(%eax) + + /* return transport_t */ + /* FCTX == EDI, DATA == ESI */ + leal 0x8(%eax), %ecx + movl %ecx, 0x1c(%eax) + + /* compute abs address of label trampoline */ + call 1f + /* address of trampoline 1 */ +1: popl %ecx + /* compute abs address of label trampoline */ + addl $trampoline-1b, %ecx + /* save address of trampoline as return address */ + /* will be entered after calling jump_fcontext() first time */ + movl %ecx, 0x18(%eax) + + /* compute abs address of label finish */ + call 2f + /* address of label 2 */ +2: popl %ecx + /* compute abs address of label finish */ + addl $finish-2b, %ecx + /* save address of finish as return-address for context-function */ + /* will be entered after context-function returns */ + movl %ecx, 0x14(%eax) + + ret /* return pointer to context-data */ + +trampoline: + /* move transport_t for entering context-function */ + movl %edi, (%esp) + movl %esi, 0x4(%esp) + pushl %ebp + /* jump to context-function */ + jmp *%ebx + +finish: + call 3f + /* address of label 3 */ +3: popl %ebx + /* compute address of GOT and store it in EBX */ + addl $_GLOBAL_OFFSET_TABLE_+[.-3b], %ebx + + /* exit code is zero */ + xorl %eax, %eax + movl %eax, (%esp) + /* exit application */ + call _exit@PLT + hlt +.size make_fcontext,.-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/ontop_i386_sysv_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/ontop_i386_sysv_elf_gas.S new file mode 100644 index 0000000000000..d3a6692ffcaf9 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86/ontop_i386_sysv_elf_gas.S @@ -0,0 +1,85 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | | * + * ---------------------------------------------------------------------------------- * + * | to | data | | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl ontop_fcontext +.align 2 +.type ontop_fcontext,@function +ontop_fcontext: + leal -0x18(%esp), %esp /* prepare stack */ + + stmxcsr (%esp) /* save MMX control- and status-word */ + fnstcw 0x4(%esp) /* save x87 control-word */ + + movl %edi, 0x8(%esp) /* save EDI */ + movl %esi, 0xc(%esp) /* save ESI */ + movl %ebx, 0x10(%esp) /* save EBX */ + movl %ebp, 0x14(%esp) /* save EBP */ + + /* store ESP (pointing to context-data) in ECX */ + movl %esp, %ecx + + /* first arg of ontop_fcontext() == fcontext to jump to */ + movl 0x20(%esp), %eax + + /* pass parent fcontext_t */ + movl %ecx, 0x20(%eax) + + /* second arg of ontop_fcontext() == data to be transferred */ + movl 0x24(%esp), %ecx + + /* pass data */ + movl %ecx, 0x24(%eax) + + /* third arg of ontop_fcontext() == ontop-function */ + movl 0x28(%esp), %ecx + + /* restore ESP (pointing to context-data) from EAX */ + movl %eax, %esp + + /* address of returned transport_t */ + movl 0x1c(%esp), %eax + /* return parent fcontext_t */ + movl %ecx, (%eax) + /* return data */ + movl %edx, 0x4(%eax) + + ldmxcsr (%esp) /* restore MMX control- and status-word */ + fldcw 0x4(%esp) /* restore x87 control-word */ + + movl 0x8(%esp), %edi /* restore EDI */ + movl 0xc(%esp), %esi /* restore ESI */ + movl 0x10(%esp), %ebx /* restore EBX */ + movl 0x14(%esp), %ebp /* restore EBP */ + + leal 0x18(%esp), %esp /* prepare stack */ + + /* jump to context */ + jmp *%ecx +.size ontop_fcontext,.-ontop_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/jump_x86_64_sysv_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/jump_x86_64_sysv_elf_gas.S new file mode 100644 index 0000000000000..019423886e9bb --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/jump_x86_64_sysv_elf_gas.S @@ -0,0 +1,76 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl jump_fcontext +.type jump_fcontext,@function +.align 16 +jump_fcontext: + leaq -0x38(%rsp), %rsp /* prepare stack */ + + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ + + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ + + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax + + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp + + movq 0x38(%rsp), %r8 /* restore return-address */ + + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ + + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ + + leaq 0x40(%rsp), %rsp /* prepare stack */ + + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi + + /* indirect jump to context */ + jmp *%r8 +.size jump_fcontext,.-jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/make_x86_64_sysv_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/make_x86_64_sysv_elf_gas.S new file mode 100644 index 0000000000000..25a0c00177fc9 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/make_x86_64_sysv_elf_gas.S @@ -0,0 +1,81 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl make_fcontext +.type make_fcontext,@function +.align 16 +make_fcontext: + /* first arg of make_fcontext() == top of context-stack */ + movq %rdi, %rax + + /* shift address in RAX to lower 16 byte boundary */ + andq $-16, %rax + + /* reserve space for context-data on context-stack */ + /* on context-function entry: (RSP -0x8) % 16 == 0 */ + leaq -0x40(%rax), %rax + + /* third arg of make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x28(%rax) + + /* save MMX control- and status-word */ + stmxcsr (%rax) + /* save x87 control-word */ + fnstcw 0x4(%rax) + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + + /* compute abs address of label finish */ + leaq finish(%rip), %rcx + /* save address of finish as return-address for context-function */ + /* will be entered after context-function returns */ + movq %rcx, 0x30(%rax) + + ret /* return pointer to context-data */ + +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + +finish: + /* exit code is zero */ + xorq %rdi, %rdi + /* exit application */ + call _exit@PLT + hlt +.size make_fcontext,.-make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/ontop_x86_64_sysv_elf_gas.S b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/ontop_x86_64_sysv_elf_gas.S new file mode 100644 index 0000000000000..d2a9373f12604 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/boost/asm/x86_64/ontop_x86_64_sysv_elf_gas.S @@ -0,0 +1,79 @@ +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************** + * * + * ---------------------------------------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ---------------------------------------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * + * ---------------------------------------------------------------------------------- * + * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * ---------------------------------------------------------------------------------- * + * ---------------------------------------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ---------------------------------------------------------------------------------- * + * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * + * ---------------------------------------------------------------------------------- * + * | R15 | RBX | RBP | RIP | * + * ---------------------------------------------------------------------------------- * + * * + ****************************************************************************************/ + +.text +.globl ontop_fcontext +.type ontop_fcontext,@function +.align 16 +ontop_fcontext: + /* preserve ontop-function in R8 */ + movq %rdx, %r8 + + leaq -0x38(%rsp), %rsp /* prepare stack */ + + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ + + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ + + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax + + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp + + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ + + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ + + leaq 0x38(%rsp), %rsp /* prepare stack */ + + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi + + /* keep return-address on stack */ + + /* indirect jump to context */ + jmp *%r8 +.size ontop_fcontext,.-ontop_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/android/ReactAndroid/src/main/jni/third-party/fmt/Android.mk b/android/ReactAndroid/src/main/jni/third-party/fmt/Android.mk new file mode 100644 index 0000000000000..11d070286749a --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/fmt/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := fmt + +LOCAL_SRC_FILES := \ + src/format.cc + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include/ +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include/ + +LOCAL_CFLAGS += -std=c++11 -fexceptions + +include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactAndroid/src/main/jni/third-party/folly/Android.mk b/android/ReactAndroid/src/main/jni/third-party/folly/Android.mk index 1bd5c3e9d137a..dacf2118dc66b 100644 --- a/android/ReactAndroid/src/main/jni/third-party/folly/Android.mk +++ b/android/ReactAndroid/src/main/jni/third-party/folly/Android.mk @@ -15,12 +15,12 @@ LOCAL_SRC_FILES:= \ folly/json_pointer.cpp \ folly/lang/CString.cpp \ folly/lang/SafeAssert.cpp \ - folly/detail/Demangle.cpp \ folly/detail/UniqueInstance.cpp \ folly/hash/SpookyHashV2.cpp \ folly/container/detail/F14Table.cpp \ folly/ScopeGuard.cpp \ - folly/portability/SysUio.cpp + folly/portability/SysUio.cpp \ + folly/lang/ToAscii.cpp ifeq ($(APP_OPTIM),debug) LOCAL_SRC_FILES += \ @@ -35,9 +35,10 @@ LOCAL_CFLAGS += -fexceptions -fno-omit-frame-pointer -frtti -Wno-sign-compare FOLLY_FLAGS := \ -DFOLLY_NO_CONFIG=1 \ -DFOLLY_HAVE_CLOCK_GETTIME=1 \ - -DFOLLY_HAVE_MEMRCHR=1 \ -DFOLLY_USE_LIBCPP=1 \ - -DFOLLY_MOBILE=1 + -DFOLLY_MOBILE=1 \ + -DFOLLY_HAVE_RECVMMSG=1 \ + -DFOLLY_HAVE_PTHREAD=1 # If APP_PLATFORM in Application.mk targets android-23 above, please comment this line. # NDK uses GNU style stderror_r() after API 23. @@ -50,11 +51,7 @@ LOCAL_EXPORT_CPPFLAGS := $(FOLLY_FLAGS) LOCAL_MODULE := libfolly_json LOCAL_SHARED_LIBRARIES := libglog libdouble-conversion -# Boost is header-only library we pretend to link is statically as -# this way android makefile will automatically setup path to boost header -# file, but except from that this will have no effect, as no c/cpp files -# are part of this static library -LOCAL_STATIC_LIBRARIES := libboost +LOCAL_STATIC_LIBRARIES := libboost libfmt include $(BUILD_SHARED_LIBRARY) @@ -62,31 +59,56 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ folly/ExceptionWrapper.cpp \ + folly/ExceptionString.cpp \ folly/Executor.cpp \ folly/SharedMutex.cpp \ + folly/Singleton.cpp \ + folly/Try.cpp \ folly/concurrency/CacheLocality.cpp \ folly/detail/AsyncTrace.cpp \ folly/detail/AtFork.cpp \ folly/detail/Futex.cpp \ folly/detail/MemoryIdler.cpp \ + folly/detail/SingletonStackTrace.cpp \ folly/detail/StaticSingletonManager.cpp \ folly/detail/ThreadLocalDetail.cpp \ + folly/fibers/Baton.cpp \ + folly/fibers/FiberManager.cpp \ + folly/fibers/Fiber.cpp \ + folly/fibers/GuardPageAllocator.cpp \ + folly/futures/detail/Core.cpp \ + folly/futures/Future.cpp \ + folly/futures/ThreadWheelTimekeeper.cpp \ folly/executors/ExecutorWithPriority.cpp \ folly/executors/InlineExecutor.cpp \ folly/executors/TimedDrivableExecutor.cpp \ folly/executors/QueuedImmediateExecutor.cpp \ + folly/io/async/AsyncTimeout.cpp \ + folly/io/async/EventBase.cpp \ + folly/io/async/EventBaseBackendBase.cpp \ + folly/io/async/EventBaseLocal.cpp \ + folly/io/async/EventHandler.cpp \ + folly/io/async/HHWheelTimer.cpp \ folly/io/async/Request.cpp \ + folly/io/async/TimeoutManager.cpp \ + folly/io/async/VirtualEventBase.cpp \ + folly/lang/Exception.cpp \ folly/memory/MallctlHelper.cpp \ folly/portability/SysMembarrier.cpp \ folly/synchronization/AsymmetricMemoryBarrier.cpp \ folly/synchronization/Hazptr.cpp \ folly/synchronization/ParkingLot.cpp \ - folly/synchronization/WaitOptions.cpp + folly/synchronization/WaitOptions.cpp \ + folly/synchronization/detail/Sleeper.cpp \ + folly/system/Pid.cpp \ + folly/system/ThreadId.cpp \ + folly/system/ThreadName.cpp + LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -fno-omit-frame-pointer -frtti -Wno-sign-compare +LOCAL_CFLAGS += -fexceptions -fno-omit-frame-pointer -frtti -Wno-sign-compare -Wno-unused-variable LOCAL_CFLAGS += $(FOLLY_FLAGS) @@ -95,14 +117,12 @@ LOCAL_EXPORT_CPPFLAGS := $(FOLLY_FLAGS) LOCAL_MODULE := libfolly_futures LOCAL_SHARED_LIBRARIES := libglog libdouble-conversion libfolly_json -# Boost is header-only library we pretend to link is statically as -# this way android makefile will automatically setup path to boost header -# file, but except from that this will have no effect, as no c/cpp files -# are part of this static library -LOCAL_STATIC_LIBRARIES := libboost +LOCAL_STATIC_LIBRARIES := libboost libevent libfmt include $(BUILD_SHARED_LIBRARY) +$(call import-module,libevent) $(call import-module,glog) $(call import-module,double-conversion) $(call import-module,boost) +$(call import-module,fmt) diff --git a/android/ReactAndroid/src/main/jni/third-party/libevent/Android.mk b/android/ReactAndroid/src/main/jni/third-party/libevent/Android.mk new file mode 100644 index 0000000000000..e546d1b711f46 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/libevent/Android.mk @@ -0,0 +1,36 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libevent + +LOCAL_SRC_FILES := event.c \ + buffer.c \ + bufferevent.c \ + bufferevent_filter.c \ + bufferevent_pair.c \ + bufferevent_ratelim.c \ + bufferevent_sock.c \ + epoll.c \ + evmap.c \ + evthread.c \ + evthread_pthread.c \ + evutil.c \ + evutil_rand.c \ + evutil_time.c \ + listener.c \ + log.c \ + poll.c \ + signal.c \ + strlcpy.c \ + select.c + +LOCAL_CFLAGS := -DNDEBUG -O2 -Wno-unused-function -Wno-unneeded-internal-declaration -std=c11 + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include/ +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include/ + +# link against libc as well +LOCAL_EXPORT_LDLIBS := -lc + +include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactAndroid/src/main/jni/third-party/libevent/evconfig-private.h b/android/ReactAndroid/src/main/jni/third-party/libevent/evconfig-private.h new file mode 100644 index 0000000000000..f6b08e353e841 --- /dev/null +++ b/android/ReactAndroid/src/main/jni/third-party/libevent/evconfig-private.h @@ -0,0 +1,49 @@ +/* evconfig-private.h. Generated from evconfig-private.h.in by configure. */ +/* evconfig-private.h template - see "Configuration Header Templates" */ +/* in AC manual. Kevin Bowling +#include +/* The size of `size_t', as computed by sizeof. */ +#if SIZE_MAX == UINT64_MAX +#define EVENT__SIZEOF_SIZE_T 8 +#elif SIZE_MAX == UINT32_MAX +#define EVENT__SIZEOF_SIZE_T 4 +#else +#error "No way to infer sizeof size_t" +#endif +#define EVENT__HAVE_UINT64_T 1 +#define EVENT__HAVE_UINT32_T 1 +#define EVENT__HAVE_UINT16_T 1 +#define EVENT__HAVE_UINT8_T 1 +#define EVENT__HAVE_UINTPTR_T 1 +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ +/* Define if libevent should build without support for a debug mode */ +/* #undef EVENT__DISABLE_DEBUG_MODE */ +/* Define if libevent should not allow replacing the mm functions */ +/* #undef EVENT__DISABLE_MM_REPLACEMENT */ +/* Define if libevent should not be compiled with thread support */ +/* #undef EVENT__DISABLE_THREAD_SUPPORT */ +/* Define to 1 if you have the `accept4' function. */ +#define EVENT__HAVE_ACCEPT4 1 +/* Define to 1 if you have the `arc4random' function. */ +#define EVENT__HAVE_ARC4RANDOM 1 +/* Define to 1 if you have the `arc4random_buf' function. */ +#define EVENT__HAVE_ARC4RANDOM_BUF 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ARPA_INET_H 1 +/* Define to 1 if you have the `clock_gettime' function. */ +#define EVENT__HAVE_CLOCK_GETTIME 1 +/* Define to 1 if you have the declaration of `CTL_KERN', and to 0 if you + don't. */ +/* #undef EVENT__HAVE_DECL_CTL_KERN */ +/* Define to 1 if you have the declaration of `KERN_ARND', and to 0 if you + don't. */ +/* #undef EVENT__HAVE_DECL_KERN_ARND */ +/* Define to 1 if you have the declaration of `KERN_RANDOM', and to 0 if you + don't. */ +/* #undef EVENT__HAVE_DECL_KERN_RANDOM */ +/* Define to 1 if you have the declaration of `RANDOM_UUID', and to 0 if you + don't. */ +/* #undef EVENT__HAVE_DECL_RANDOM_UUID */ +/* Define if /dev/poll is available */ +/* #undef EVENT__HAVE_DEVPOLL */ +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_DLFCN_H 1 +/* Define if your system supports the epoll system calls */ +#define EVENT__HAVE_EPOLL 1 +/* Define to 1 if you have the `epoll_create1' function. */ +#define EVENT__HAVE_EPOLL_CREATE1 1 +/* Define to 1 if you have the `epoll_ctl' function. */ +#define EVENT__HAVE_EPOLL_CTL 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ERRNO_H 1 +/* Define to 1 if you have ERR_remove_thread_stat(). */ +#undef EVENT__HAVE_ERR_REMOVE_THREAD_STATE +/* Define to 1 if you have the `eventfd' function. */ +#define EVENT__HAVE_EVENTFD 1 +/* Define if your system supports event ports */ +/* #undef EVENT__HAVE_EVENT_PORTS */ +/* Define to 1 if you have the `fcntl' function. */ +#define EVENT__HAVE_FCNTL 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_FCNTL_H 1 +/* Define to 1 if the system has the type `fd_mask'. */ +/* #undef EVENT__HAVE_FD_MASK */ +/* Do we have getaddrinfo()? */ +#define EVENT__HAVE_GETADDRINFO 1 +/* Define to 1 if you have the `getegid' function. */ +#define EVENT__HAVE_GETEGID 1 +/* Define to 1 if you have the `geteuid' function. */ +#define EVENT__HAVE_GETEUID 1 +/* Define this if you have any gethostbyname_r() */ +#define EVENT__HAVE_GETHOSTBYNAME_R 1 +/* Define this if gethostbyname_r takes 3 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_3_ARG */ +/* Define this if gethostbyname_r takes 5 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_5_ARG */ +/* Define this if gethostbyname_r takes 6 arguments */ +#define EVENT__HAVE_GETHOSTBYNAME_R_6_ARG 1 +/* Define to 1 if you have the `getifaddrs' function. */ +/* #undef EVENT__HAVE_GETIFADDRS */ +/* Define to 1 if you have the `getnameinfo' function. */ +#define EVENT__HAVE_GETNAMEINFO 1 +/* Define to 1 if you have the `getprotobynumber' function. */ +#define EVENT__HAVE_GETPROTOBYNUMBER 1 +/* Define to 1 if you have the `getservbyname' function. */ +/* #undef EVENT__HAVE_GETSERVBYNAME */ +/* Define to 1 if you have the `gettimeofday' function. */ +#define EVENT__HAVE_GETTIMEOFDAY 1 +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_IFADDRS_H */ +/* Define to 1 if you have the `inet_ntop' function. */ +#define EVENT__HAVE_INET_NTOP 1 +/* Define to 1 if you have the `inet_pton' function. */ +#define EVENT__HAVE_INET_PTON 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_INTTYPES_H 1 +/* Define to 1 if you have the `issetugid' function. */ +/* #undef EVENT__HAVE_ISSETUGID */ +/* Define to 1 if you have the `kqueue' function. */ +/* #undef EVENT__HAVE_KQUEUE */ +/* Define if the system has zlib */ +#define EVENT__HAVE_LIBZ 1 +/* Define to 1 if you have the `mach_absolute_time' function. */ +#undef HAVE_MACH_ABSOLUTE_TIME +/* Define to 1 if you have the header file. */ +#undef HAVE_MACH_MACH_TIME_H +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_MEMORY_H 1 +/* Define to 1 if you have the `mmap' function. */ +#define EVENT__HAVE_MMAP 1 +/* Define to 1 if you have the `nanosleep' function. */ +#define EVENT__HAVE_NANOSLEEP 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETDB_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_IN6_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_IN_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_TCP_H 1 +/* Define if the system has openssl */ +#define EVENT__HAVE_OPENSSL 1 +/* Define to 1 if you have the `pipe' function. */ +/* #undef EVENT__HAVE_PIPE */ +/* Define to 1 if you have the `pipe2' function. */ +/* #undef EVENT__HAVE_PIPE2 */ +/* Define to 1 if you have the `poll' function. */ +#define EVENT__HAVE_POLL 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_POLL_H 1 +/* Define to 1 if you have the `port_create' function. */ +/* #undef EVENT__HAVE_PORT_CREATE */ +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_PORT_H */ +/* Define if you have POSIX threads libraries and header files. */ +/* #undef EVENT__HAVE_PTHREAD */ +/* Define if we have pthreads on this system */ +#define EVENT__HAVE_PTHREADS 1 +/* Define to 1 if you have the `putenv' function. */ +#define EVENT__HAVE_PUTENV 1 +/* Define to 1 if the system has the type `sa_family_t'. */ +#define EVENT__HAVE_SA_FAMILY_T 1 +/* Define to 1 if you have the `select' function. */ +#define EVENT__HAVE_SELECT 1 +/* Define to 1 if you have the `sendfile' function. */ +#define EVENT__HAVE_SENDFILE 1 +/* Define to 1 if you have the `setenv' function. */ +#define EVENT__HAVE_SETENV 1 +/* Define if F_SETFD is defined in */ +#define EVENT__HAVE_SETFD 1 +/* Define to 1 if you have the `setrlimit' function. */ +#define HAVE_SETRLIMIT 1 +/* Define to 1 if you have the `sigaction' function. */ +#define EVENT__HAVE_SIGACTION 1 +/* Define to 1 if you have the `signal' function. */ +#define EVENT__HAVE_SIGNAL 1 +/* Define to 1 if you have the `splice' function. */ +#define EVENT__HAVE_SPLICE 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDARG_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDDEF_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDINT_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDLIB_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRINGS_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRING_H 1 +/* Define to 1 if you have the `strlcpy' function. */ +#define EVENT__HAVE_STRLCPY 1 +/* Define to 1 if you have the `strsep' function. */ +#define EVENT__HAVE_STRSEP 1 +/* Define to 1 if you have the `strtok_r' function. */ +#define EVENT__HAVE_STRTOK_R 1 +/* Define to 1 if you have the `strtoll' function. */ +#define EVENT__HAVE_STRTOLL 1 +/* Define to 1 if the system has the type `struct addrinfo'. */ +#define EVENT__HAVE_STRUCT_ADDRINFO 1 +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR 1 +/* Define to 1 if `s6_addr16' is a member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 1 +/* Define to 1 if `s6_addr32' is a member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 1 +/* Define to 1 if the system has the type `struct sockaddr_in6'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN6 1 +/* Define to 1 if `sin6_len' is a member of `struct sockaddr_in6'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN */ +/* Define to 1 if `sin_len' is a member of `struct sockaddr_in'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE 1 +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ +/* Define to 1 if the system has the type `struct so_linger'. */ +#define HAVE_STRUCT_SO_LINGER 1 +/* Define to 1 if you have the `sysctl' function. */ +/* #undef EVENT__HAVE_SYSCTL */ +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_DEVPOLL_H */ +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EPOLL_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EVENTFD_H 1 +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EVENT_H */ +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_IOCTL_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_MMAN_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_PARAM_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_QUEUE_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SELECT_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SENDFILE_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SOCKET_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_STAT_H 1 +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SYSCTL_H */ +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMERFD_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TIME_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TYPES_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_UIO_H 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_WAIT_H 1 +/* Define if TAILQ_FOREACH is defined in */ +/*#define EVENT__HAVE_TAILQFOREACH 1 */ +/* Define if timeradd is defined in */ +#define EVENT__HAVE_TIMERADD 1 +/* Define if timerclear is defined in */ +#define EVENT__HAVE_TIMERCLEAR 1 +/* Define if timercmp is defined in */ +#define EVENT__HAVE_TIMERCMP 1 +/* Define to 1 if you have the `timerfd_create' function. */ +#define HAVE_TIMERFD_CREATE 1 +/* Define if timerisset is defined in */ +#define EVENT__HAVE_TIMERISSET 1 +/* Define to 1 if the system has the type `uint16_t'. */ +#define EVENT__HAVE_UINT16_T 1 +/* Define to 1 if the system has the type `uint32_t'. */ +#define EVENT__HAVE_UINT32_T 1 +/* Define to 1 if the system has the type `uint64_t'. */ +#define EVENT__HAVE_UINT64_T 1 +/* Define to 1 if the system has the type `uint8_t'. */ +#define EVENT__HAVE_UINT8_T 1 +/* Define to 1 if the system has the type `uintptr_t'. */ +#define EVENT__HAVE_UINTPTR_T 1 +/* Define to 1 if you have the `umask' function. */ +#define EVENT__HAVE_UMASK 1 +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_UNISTD_H 1 +/* Define to 1 if you have the `unsetenv' function. */ +#define EVENT__HAVE_UNSETENV 1 +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 +/* Define to 1 if you have the `vasprintf' function. */ +#define EVENT__HAVE_VASPRINTF 1 +/* Define if waitpid() supports WNOWAIT */ +#define HAVE_WAITPID_WITH_WNOWAIT 1 +/* Define if kqueue works correctly with pipes */ +/* #undef EVENT__HAVE_WORKING_KQUEUE */ +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ZLIB_H 1 +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define EVENT__LT_OBJDIR ".libs/" +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef EVENT__NO_MINUS_C_MINUS_O */ +/* Numeric representation of the version */ +#define EVENT__NUMERIC_VERSION 0x02010c00 +/* Name of package */ +#define EVENT__PACKAGE "libevent" +/* Define to the address where bug reports for this package should be sent. */ +#define EVENT__PACKAGE_BUGREPORT "" +/* Define to the full name of this package. */ +#define EVENT__PACKAGE_NAME "" +/* Define to the full name and version of this package. */ +#define EVENT__PACKAGE_STRING "" +/* Define to the one symbol short name of this package. */ +#define EVENT__PACKAGE_TARNAME "" +/* Define to the home page for this package. */ +#define EVENT__PACKAGE_URL "" +/* Define to the version of this package. */ +#define EVENT__PACKAGE_VERSION "" +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef EVENT__PTHREAD_CREATE_JOINABLE */ +/* Define to 1 if you have the ANSI C header files. */ +#define EVENT__STDC_HEADERS 1 +/* Define to 1 if you can safely include both and . */ +#define EVENT__TIME_WITH_SYS_TIME 1 +/* Version number of package */ +#define EVENT__VERSION "2.1.12p-stable" +/* Define to appropriate substitue if compiler doesnt have __func__ */ +/* #undef EVENT____func__ */ +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef EVENT__const */ +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef EVENT____cplusplus +/* #undef EVENT__inline */ +#endif +/* Define to `int' if does not define. */ +/* #undef EVENT__pid_t */ +/* Define to `unsigned int' if does not define. */ +/* #undef EVENT__size_t */ +/* Define to unsigned int if you dont have it */ +/* #undef EVENT__socklen_t */ +/* Define to `int' if does not define. */ +/* #undef EVENT__ssize_t */ +#endif /* event2/event-config.h */ diff --git a/android/ReactAndroid/src/main/libraries/fbcore/src/main/java/com/facebook/common/logging/BUCK b/android/ReactAndroid/src/main/libraries/fbcore/src/main/java/com/facebook/common/logging/BUCK index e8c4380c800a5..e18c8ef06ee15 100644 --- a/android/ReactAndroid/src/main/libraries/fbcore/src/main/java/com/facebook/common/logging/BUCK +++ b/android/ReactAndroid/src/main/libraries/fbcore/src/main/java/com/facebook/common/logging/BUCK @@ -2,6 +2,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "logging", + autoglob = False, visibility = ["//ReactAndroid/..."], exported_deps = [ react_native_dep("libraries/fresco/fresco-react-native:fbcore"), diff --git a/android/ReactAndroid/src/main/libraries/fbjni/BUCK b/android/ReactAndroid/src/main/libraries/fbjni/BUCK index db32c1b4a5463..4d6d134811ab5 100644 --- a/android/ReactAndroid/src/main/libraries/fbjni/BUCK +++ b/android/ReactAndroid/src/main/libraries/fbjni/BUCK @@ -15,6 +15,6 @@ fb_native.android_prebuilt_aar( fb_native.remote_file( name = "fbjni-binary-aar", - sha1 = "d9e1b6ebbe55fe25f6ee6257ef64588e0f8a8db0", - url = "mvn:com.facebook.fbjni:fbjni:aar:0.0.2", + sha1 = "b20ae3406d911a28315b6ab53f122075500bfa27", + url = "mvn:com.facebook.fbjni:fbjni:aar:0.2.2", ) diff --git a/android/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK b/android/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK index 830cfd9da3f4f..7c8afefbb1fc8 100644 --- a/android/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK +++ b/android/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK @@ -9,8 +9,8 @@ rn_android_prebuilt_aar( fb_native.remote_file( name = "fresco-binary-aar", - sha1 = "d473020b37b7cdd3171154942b55021a55a9d990", - url = "mvn:com.facebook.fresco:fresco:aar:2.0.0", + sha1 = "b768459446d166d148aaf3b8edcdcecd59381190", + url = "mvn:com.facebook.fresco:fresco:aar:2.5.0", ) rn_android_prebuilt_aar( @@ -21,18 +21,25 @@ rn_android_prebuilt_aar( fb_native.remote_file( name = "drawee-binary-aar", - sha1 = "a85bfaeb87a9c8d1521c70edf6ded91ff9999475", - url = "mvn:com.facebook.fresco:drawee:aar:2.0.0", + sha1 = "06971aca0134eafa61c1b81714c0954cb4eb4e10", + url = "mvn:com.facebook.fresco:drawee:aar:2.5.0", ) rn_android_library( name = "imagepipeline", + autoglob = False, visibility = ["//ReactAndroid/..."], exported_deps = [ ":bolts", ":imagepipeline-base", ":imagepipeline-core", + ":imagepipeline-native", + ":memory-type-ashmem", + ":memory-type-java", + ":memory-type-native", ":native-filters", + ":native-transcoder", + ":ui-common", ], ) @@ -44,8 +51,8 @@ rn_android_prebuilt_aar( fb_native.remote_file( name = "imagepipeline-base-aar", - sha1 = "d27635390665d433f987177c548d25d0473eadbe", - url = "mvn:com.facebook.fresco:imagepipeline-base:aar:2.0.0", + sha1 = "6839693f5d3b6697cfabbf159d004dae2f465126", + url = "mvn:com.facebook.fresco:imagepipeline-base:aar:2.5.0", ) rn_android_prebuilt_aar( @@ -56,8 +63,8 @@ rn_android_prebuilt_aar( fb_native.remote_file( name = "imagepipeline-aar", - sha1 = "7bc59327fb4895c465cbfeede700daf349ea56da", - url = "mvn:com.facebook.fresco:imagepipeline:aar:2.0.0", + sha1 = "dd62dd1607ade4532c1240f137ac3c2d7920c134", + url = "mvn:com.facebook.fresco:imagepipeline:aar:2.5.0", ) rn_android_prebuilt_aar( @@ -68,8 +75,20 @@ rn_android_prebuilt_aar( remote_file( name = "nativeimagefilters-aar", - sha1 = "f49525db580abc4d2fb0a74fac771fc6c69f2adb", - url = "mvn:com.facebook.fresco:nativeimagefilters:aar:2.0.0", + sha1 = "4a30438df8d960000b37b1f8cdba9d7996d57ea9", + url = "mvn:com.facebook.fresco:nativeimagefilters:aar:2.5.0", +) + +rn_android_prebuilt_aar( + name = "native-transcoder", + aar = ":nativeimagetranscoder-aar", + visibility = ["//ReactAndroid/..."], +) + +remote_file( + name = "nativeimagetranscoder-aar", + sha1 = "7db353c68b41e2ad502a959c554387d59e75eb75", + url = "mvn:com.facebook.fresco:nativeimagetranscoder:aar:2.5.0", ) rn_prebuilt_jar( @@ -92,8 +111,20 @@ rn_android_prebuilt_aar( fb_native.remote_file( name = "fbcore-aar", - sha1 = "8de91f71e8aa84a4d9be4dd88d1a0ac51600ad60", - url = "mvn:com.facebook.fresco:fbcore:aar:2.0.0", + sha1 = "ffe378b572055e0600377c000d390fb46bf278a5", + url = "mvn:com.facebook.fresco:fbcore:aar:2.5.0", +) + +rn_android_prebuilt_aar( + name = "imagepipeline-native", + aar = ":imagepipeline-native-binary-aar", + visibility = ["//ReactAndroid/..."], +) + +fb_native.remote_file( + name = "imagepipeline-native-binary-aar", + sha1 = "f8e65e897a883ac4f754d57897aaba0016c8beba", + url = "mvn:com.facebook.fresco:imagepipeline-native:aar:2.5.0", ) rn_android_prebuilt_aar( @@ -104,6 +135,54 @@ rn_android_prebuilt_aar( fb_native.remote_file( name = "imagepipeline-okhttp3-binary-aar", - sha1 = "bc1212ca66cd09678b416894ea8bd04102d26c5f", - url = "mvn:com.facebook.fresco:imagepipeline-okhttp3:aar:2.0.0", + sha1 = "8e274a77ffbe9f8334e09855b2a811b2a63a5708", + url = "mvn:com.facebook.fresco:imagepipeline-okhttp3:aar:2.5.0", +) + +rn_android_prebuilt_aar( + name = "memory-type-ashmem", + aar = ":memory-type-ashmem-aar", + visibility = ["//ReactAndroid/..."], +) + +remote_file( + name = "memory-type-ashmem-aar", + sha1 = "b43b53aca89569fd2e8e964b71dc7afbf62204e8", + url = "mvn:com.facebook.fresco:memory-type-ashmem:aar:2.5.0", +) + +rn_android_prebuilt_aar( + name = "memory-type-java", + aar = ":memory-type-java-aar", + visibility = ["//ReactAndroid/..."], +) + +remote_file( + name = "memory-type-java-aar", + sha1 = "57537a8b69dbad44fafdaea3067a776e11586465", + url = "mvn:com.facebook.fresco:memory-type-java:aar:2.5.0", +) + +rn_android_prebuilt_aar( + name = "memory-type-native", + aar = ":memory-type-native-aar", + visibility = ["//ReactAndroid/..."], +) + +remote_file( + name = "memory-type-native-aar", + sha1 = "7eb7c52ee2a4d40b7f706c90069549410236aaa5", + url = "mvn:com.facebook.fresco:memory-type-native:aar:2.5.0", +) + +rn_android_prebuilt_aar( + name = "ui-common", + aar = ":ui-common-aar", + visibility = ["//ReactAndroid/..."], +) + +remote_file( + name = "ui-common-aar", + sha1 = "224851cf4c074aeff5dc42861b557870459a9d28", + url = "mvn:com.facebook.fresco:ui-common:aar:2.5.0", ) diff --git a/android/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK b/android/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK index 3c1ec406b6898..61fc9b193d3c2 100644 --- a/android/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK +++ b/android/ReactAndroid/src/main/libraries/soloader/java/com/facebook/soloader/BUCK @@ -28,17 +28,17 @@ fb_native.android_prebuilt_aar( fb_native.remote_file( name = "annotation-binary.jar", sha1 = "dc58463712cb3e5f03d8ee5ac9743b9ced9afa77", - url = "mvn:com.facebook.soloader:annotation:jar:0.9.0", + url = "mvn:com.facebook.soloader:annotation:jar:0.10.1", ) fb_native.remote_file( name = "nativeloader-binary.jar", - sha1 = "677c7fbfcc847d7eb6082048d07b10afd4cff898", - url = "mvn:com.facebook.soloader:nativeloader:jar:0.9.0", + sha1 = "ba1b31b4b9f65494a90de7c2728b57155344a858", + url = "mvn:com.facebook.soloader:nativeloader:jar:0.10.1", ) fb_native.remote_file( name = "soloader-binary-aar", - sha1 = "6e138af1dd29ceabf5bace2d24dc4333f304d104", - url = "mvn:com.facebook.soloader:soloader:aar:0.9.0", + sha1 = "78e5537c0cdf7c190f6cad9a2490f9f84ee8f041", + url = "mvn:com.facebook.soloader:soloader:aar:0.10.1", ) diff --git a/android/ReactAndroid/src/main/res/devsupport/values/strings.xml b/android/ReactAndroid/src/main/res/devsupport/values/strings.xml index 83de150448dea..b53bee50c55c0 100644 --- a/android/ReactAndroid/src/main/res/devsupport/values/strings.xml +++ b/android/ReactAndroid/src/main/res/devsupport/values/strings.xml @@ -16,7 +16,8 @@ Disable Fast Refresh Disabling Fast Refresh because it requires a development bundle. Switching to development bundle in order to enable Fast Refresh. - Toggle Inspector + Show Element Inspector + Hide Element Inspector Show Perf Monitor Hide Perf Monitor Settings diff --git a/android/ReactAndroid/src/main/res/views/uimanager/values/strings_unlocalized.xml b/android/ReactAndroid/src/main/res/views/uimanager/values/strings_unlocalized.xml index c30cd3d8819ca..ad93dfb42c7bf 100644 --- a/android/ReactAndroid/src/main/res/views/uimanager/values/strings_unlocalized.xml +++ b/android/ReactAndroid/src/main/res/views/uimanager/values/strings_unlocalized.xml @@ -4,18 +4,10 @@ name="reactandroid_link_description" translatable="false" >Link - Search Field Image - Button collapsed + unselected () { @Override diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK b/android/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK index c774e197673e5..c89522bca2fc8 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK +++ b/android/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK @@ -10,12 +10,13 @@ rn_android_library( ["*.java"], exclude = STANDARD_TEST_SRCS, ), + autoglob = False, visibility = [ "PUBLIC", ], deps = [ react_native_android_toplevel_dep("third-party/java/mockito2:mockito2"), - react_native_dep("third-party/java/robolectric/4.4:robolectric"), + react_native_dep("third-party/java/robolectric:robolectric"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_tests_target("java/org/mockito/configuration:configuration"), diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.java b/android/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.java index ffce9ea596a15..d3ab1b7fe70e5 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.java @@ -52,6 +52,7 @@ public void handleException(Exception e) { when(reactInstance.getReactQueueConfiguration()).thenReturn(ReactQueueConfiguration); when(reactInstance.getNativeModule(UIManagerModule.class)) .thenReturn(mock(UIManagerModule.class)); + when(reactInstance.isDestroyed()).thenReturn(false); return reactInstance; } diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java index f155eebbddd31..53a1340330911 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java @@ -11,6 +11,7 @@ import com.facebook.react.common.JavascriptException; import java.util.HashMap; +import okhttp3.WebSocket; import okio.ByteString; import org.junit.Rule; import org.junit.Test; @@ -74,7 +75,8 @@ public void test_executeJSCall_ShouldSendCorrectMessage() throws Exception { public void test_onMessage_WithInvalidContentType_ShouldNotTriggerCallbacks() throws Exception { JSDebuggerWebSocketClient client = PowerMockito.spy(new JSDebuggerWebSocketClient()); - client.onMessage(null, ByteString.encodeUtf8("{\"replyID\":0, \"result\":\"OK\"}")); + client.onMessage( + mock(WebSocket.class), ByteString.encodeUtf8("{\"replyID\":0, \"result\":\"OK\"}")); PowerMockito.verifyPrivate(client, never()) .invoke("triggerRequestSuccess", anyInt(), nullable(String.class)); PowerMockito.verifyPrivate(client, never()).invoke("triggerRequestFailure", anyInt(), any()); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK b/android/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK index 750600054fbf2..f50c34f262d13 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK +++ b/android/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK @@ -32,6 +32,7 @@ rn_robolectric_test( react_native_target("java/com/facebook/react/modules/common:common"), react_native_target("java/com/facebook/react/modules/core:core"), react_native_target("java/com/facebook/react/modules/debug:debug"), + react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"), react_native_target("java/com/facebook/react/modules/dialog:dialog"), react_native_target("java/com/facebook/react/modules/network:network"), react_native_target("java/com/facebook/react/modules/share:share"), diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/modules/clipboard/ClipboardModuleTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/modules/clipboard/ClipboardModuleTest.java index fb84ba47caaeb..21ab31a27b839 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/modules/clipboard/ClipboardModuleTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/modules/clipboard/ClipboardModuleTest.java @@ -13,6 +13,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.text.ClipboardManager; +import com.facebook.react.bridge.ReactApplicationContext; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,7 +33,8 @@ public class ClipboardModuleTest { @Before public void setUp() { - mClipboardModule = new ClipboardModule(RuntimeEnvironment.application); + mClipboardModule = + new ClipboardModule(new ReactApplicationContext(RuntimeEnvironment.application)); mClipboardManager = (ClipboardManager) RuntimeEnvironment.application.getSystemService(Context.CLIPBOARD_SERVICE); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.java new file mode 100644 index 0000000000000..6ed814abb5340 --- /dev/null +++ b/android/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.deviceinfo; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.CatalystInstance; +import com.facebook.react.bridge.JavaOnlyMap; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactTestHelper; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.uimanager.DisplayMetricsHolder; +import java.util.Arrays; +import java.util.List; +import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +@PrepareForTest({Arguments.class, DisplayMetricsHolder.class}) +@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "androidx.*", "android.*"}) +public class DeviceInfoModuleTest extends TestCase { + + @Rule public PowerMockRule rule = new PowerMockRule(); + + private DeviceInfoModule mDeviceInfoModule; + private DeviceEventManagerModule.RCTDeviceEventEmitter mRCTDeviceEventEmitterMock; + + private WritableMap fakePortraitDisplayMetrics; + private WritableMap fakeLandscapeDisplayMetrics; + + @Before + public void setUp() { + initTestData(); + + mockStatic(DisplayMetricsHolder.class); + + mRCTDeviceEventEmitterMock = mock(DeviceEventManagerModule.RCTDeviceEventEmitter.class); + + final ReactApplicationContext context = + spy(new ReactApplicationContext(RuntimeEnvironment.application)); + CatalystInstance catalystInstanceMock = ReactTestHelper.createMockCatalystInstance(); + context.initializeWithInstance(catalystInstanceMock); + when(context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)) + .thenReturn(mRCTDeviceEventEmitterMock); + + mDeviceInfoModule = new DeviceInfoModule(context); + } + + @After + public void teardown() { + DisplayMetricsHolder.setWindowDisplayMetrics(null); + DisplayMetricsHolder.setScreenDisplayMetrics(null); + } + + @Test + public void test_itDoesNotEmitAnEvent_whenDisplayMetricsNotChanged() { + givenDisplayMetricsHolderContains(fakePortraitDisplayMetrics); + + mDeviceInfoModule.getTypedExportedConstants(); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + + verifyNoMoreInteractions(mRCTDeviceEventEmitterMock); + } + + @Test + public void test_itEmitsOneEvent_whenDisplayMetricsChangedOnce() { + givenDisplayMetricsHolderContains(fakePortraitDisplayMetrics); + + mDeviceInfoModule.getTypedExportedConstants(); + givenDisplayMetricsHolderContains(fakeLandscapeDisplayMetrics); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + + verifyUpdateDimensionsEventsEmitted(mRCTDeviceEventEmitterMock, fakeLandscapeDisplayMetrics); + } + + @Test + public void test_itEmitsJustOneEvent_whenUpdateRequestedMultipleTimes() { + givenDisplayMetricsHolderContains(fakePortraitDisplayMetrics); + mDeviceInfoModule.getTypedExportedConstants(); + givenDisplayMetricsHolderContains(fakeLandscapeDisplayMetrics); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + + verifyUpdateDimensionsEventsEmitted(mRCTDeviceEventEmitterMock, fakeLandscapeDisplayMetrics); + } + + @Test + public void test_itEmitsMultipleEvents_whenDisplayMetricsChangedBetweenUpdates() { + givenDisplayMetricsHolderContains(fakePortraitDisplayMetrics); + + mDeviceInfoModule.getTypedExportedConstants(); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + givenDisplayMetricsHolderContains(fakeLandscapeDisplayMetrics); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + givenDisplayMetricsHolderContains(fakePortraitDisplayMetrics); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + givenDisplayMetricsHolderContains(fakeLandscapeDisplayMetrics); + mDeviceInfoModule.emitUpdateDimensionsEvent(); + + verifyUpdateDimensionsEventsEmitted( + mRCTDeviceEventEmitterMock, + fakeLandscapeDisplayMetrics, + fakePortraitDisplayMetrics, + fakeLandscapeDisplayMetrics); + } + + private static void givenDisplayMetricsHolderContains(final WritableMap fakeDisplayMetrics) { + when(DisplayMetricsHolder.getDisplayMetricsWritableMap(1.0)).thenReturn(fakeDisplayMetrics); + } + + private static void verifyUpdateDimensionsEventsEmitted( + DeviceEventManagerModule.RCTDeviceEventEmitter emitter, WritableMap... expectedEvents) { + List expectedEventList = Arrays.asList(expectedEvents); + ArgumentCaptor captor = ArgumentCaptor.forClass(WritableMap.class); + verify(emitter, times(expectedEventList.size())) + .emit(eq("didUpdateDimensions"), captor.capture()); + + List actualEvents = captor.getAllValues(); + assertThat(actualEvents).isEqualTo(expectedEventList); + } + + private void initTestData() { + mockStatic(Arguments.class); + when(Arguments.createMap()) + .thenAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return new JavaOnlyMap(); + } + }); + + fakePortraitDisplayMetrics = Arguments.createMap(); + fakePortraitDisplayMetrics.putInt("width", 100); + fakePortraitDisplayMetrics.putInt("height", 200); + + fakeLandscapeDisplayMetrics = Arguments.createMap(); + fakeLandscapeDisplayMetrics.putInt("width", 200); + fakeLandscapeDisplayMetrics.putInt("height", 100); + } +} diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java index 619b464d1d7c0..6f12eee70cece 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java @@ -60,7 +60,7 @@ public void setUp() throws Exception { mActivity = mActivityController.create().start().resume().get(); final ReactApplicationContext context = PowerMockito.mock(ReactApplicationContext.class); - PowerMockito.when(context.hasActiveCatalystInstance()).thenReturn(true); + PowerMockito.when(context.hasActiveReactInstance()).thenReturn(true); PowerMockito.when(context, "getCurrentActivity").thenReturn(mActivity); mDialogModule = new DialogModule(context); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java index a6e401ed9b0c8..eb09e6dee01f9 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java @@ -76,8 +76,8 @@ public class NetworkingModuleTest { @Before public void prepareModules() { - mHttpClient = mock(OkHttpClient.class); - when(mHttpClient.cookieJar()).thenReturn(mock(CookieJarContainer.class)); + mHttpClient = PowerMockito.mock(OkHttpClient.class); + PowerMockito.when(mHttpClient.cookieJar()).thenReturn(mock(CookieJarContainer.class)); when(mHttpClient.newCall(any(Request.class))) .thenAnswer( new Answer() { @@ -87,8 +87,8 @@ public Object answer(InvocationOnMock invocation) throws Throwable { return callMock; } }); - OkHttpClient.Builder clientBuilder = mock(OkHttpClient.Builder.class); - when(clientBuilder.build()).thenReturn(mHttpClient); + OkHttpClient.Builder clientBuilder = PowerMockito.mock(OkHttpClient.Builder.class); + PowerMockito.when(clientBuilder.build()).thenReturn(mHttpClient); when(mHttpClient.newBuilder()).thenReturn(clientBuilder); mEmitter = mock(RCTDeviceEventEmitter.class); @@ -96,7 +96,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { CatalystInstance reactInstance = mock(CatalystInstance.class); ReactApplicationContext reactContext = mock(ReactApplicationContext.class); when(reactContext.getCatalystInstance()).thenReturn(reactInstance); - when(reactContext.hasActiveCatalystInstance()).thenReturn(true); + when(reactContext.hasActiveReactInstance()).thenReturn(true); when(reactContext.getJSModule(any(Class.class))).thenReturn(mEmitter); mNetworkingModule = new NetworkingModule(reactContext, "", mHttpClient); } @@ -453,7 +453,7 @@ public void testMultipartPostRequestBody() throws Exception { .thenCallRealMethod(); when(inputStream.available()).thenReturn("imageUri".length()); - final MultipartBody.Builder multipartBuilder = mock(MultipartBody.Builder.class); + final MultipartBody.Builder multipartBuilder = PowerMockito.mock(MultipartBody.Builder.class); PowerMockito.whenNew(MultipartBody.Builder.class) .withNoArguments() .thenReturn(multipartBuilder); @@ -545,7 +545,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } @Test - public void testCancelAllCallsOnCatalystInstanceDestroy() throws Exception { + public void testCancelAllCallsInvalidate() throws Exception { PowerMockito.mockStatic(OkHttpCallUtil.class); final int requests = 3; final Call[] calls = new Call[requests]; @@ -578,7 +578,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } verify(mHttpClient, times(3)).newCall(any(Request.class)); - mNetworkingModule.onCatalystInstanceDestroy(); + mNetworkingModule.invalidate(); PowerMockito.verifyStatic(OkHttpCallUtil.class, times(3)); ArgumentCaptor clientArguments = ArgumentCaptor.forClass(OkHttpClient.class); ArgumentCaptor requestIdArguments = ArgumentCaptor.forClass(Integer.class); @@ -591,7 +591,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } @Test - public void testCancelSomeCallsOnCatalystInstanceDestroy() throws Exception { + public void testCancelSomeCallsInvalidate() throws Exception { PowerMockito.mockStatic(OkHttpCallUtil.class); final int requests = 3; final Call[] calls = new Call[requests]; @@ -634,7 +634,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { // verifyStatic actually does not clear all calls so far, so we have to check for all of them. // If `cancelTag` would've been called again for the aborted call, we would have had // `requests + 1` calls. - mNetworkingModule.onCatalystInstanceDestroy(); + mNetworkingModule.invalidate(); PowerMockito.verifyStatic(OkHttpCallUtil.class, times(requests)); clientArguments = ArgumentCaptor.forClass(OkHttpClient.class); requestIdArguments = ArgumentCaptor.forClass(Integer.class); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java index 7a98a0f2ba86e..7164b8489af44 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java @@ -75,7 +75,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { CatalystInstance reactInstance = mock(CatalystInstance.class); ReactApplicationContext reactContext = mock(ReactApplicationContext.class); when(reactContext.getCatalystInstance()).thenReturn(reactInstance); - when(reactContext.hasActiveCatalystInstance()).thenReturn(true); + when(reactContext.hasActiveReactInstance()).thenReturn(true); mCurrentTimeNs = 0; mPostFrameCallbackHandler = new PostFrameCallbackHandler(); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/BaseViewManagerTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/BaseViewManagerTest.java index 91d5ab9b0ea19..97e5705be2f60 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/BaseViewManagerTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/BaseViewManagerTest.java @@ -10,19 +10,33 @@ import static org.fest.assertions.api.Assertions.assertThat; import com.facebook.react.R; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.JavaOnlyMap; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole; import com.facebook.react.views.view.ReactViewGroup; import com.facebook.react.views.view.ReactViewManager; import java.util.Locale; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +@PrepareForTest({Arguments.class}) @RunWith(RobolectricTestRunner.class) +@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "androidx.*", "android.*"}) public class BaseViewManagerTest { + @Rule public PowerMockRule rule = new PowerMockRule(); + private BaseViewManager mViewManager; private ReactViewGroup mView; @@ -30,6 +44,15 @@ public class BaseViewManagerTest { public void setUp() { mViewManager = new ReactViewManager(); mView = new ReactViewGroup(RuntimeEnvironment.application); + PowerMockito.mockStatic(Arguments.class); + PowerMockito.when(Arguments.createMap()) + .thenAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return new JavaOnlyMap(); + } + }); } @Test @@ -44,4 +67,13 @@ public void testAccessibilityRoleTurkish() { mViewManager.setAccessibilityRole(mView, "image"); assertThat(mView.getTag(R.id.reactandroid_accessibility_role)).isEqualTo(AccessibilityRole.IMAGE); } + + @Test + public void testAccessibilityStateSelected() { + WritableMap accessibilityState = Arguments.createMap(); + accessibilityState.putBoolean("selected", true); + mViewManager.setViewState(mView, accessibilityState); + assertThat(mView.getTag(R.id.accessibility_state)).isEqualTo(accessibilityState); + assertThat(mView.isSelected()).isEqualTo(true); + } } diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java index ed1d23990e437..91be74945d9c9 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java @@ -36,6 +36,8 @@ public class SimpleViewPropertyTest { @Rule public PowerMockRule rule = new PowerMockRule(); + private static int sViewTag = 2; + private static class ConcreteViewManager extends SimpleViewManager { @ReactProp(name = "foo") @@ -75,7 +77,9 @@ public ReactStylesDiffMap buildStyles(Object... keysAndValues) { @Test public void testOpacity() { - View view = mManager.createView(mThemedContext, buildStyles(), null, new JSResponderHandler()); + View view = + mManager.createView( + sViewTag, mThemedContext, buildStyles(), null, new JSResponderHandler()); mManager.updateProperties(view, buildStyles()); assertThat(view.getAlpha()).isEqualTo(1.0f); @@ -89,7 +93,9 @@ public void testOpacity() { @Test public void testBackgroundColor() { - View view = mManager.createView(mThemedContext, buildStyles(), null, new JSResponderHandler()); + View view = + mManager.createView( + sViewTag, mThemedContext, buildStyles(), null, new JSResponderHandler()); mManager.updateProperties(view, buildStyles()); assertThat(view.getBackground()).isEqualTo(null); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java index b7d86db7c28ba..e172c90a3c790 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java @@ -120,6 +120,14 @@ public void testRoundedCorners() { viewManager.updateProperties(view, buildStyles("borderRadius", null)); } + @Test + public void testAccessibilityFocus() { + ReactImageManager viewManager = new ReactImageManager(); + ReactImageView view = viewManager.createViewInstance(mThemeContext); + viewManager.setAccessible(view, true); + assertEquals(true, view.isFocusable()); + } + @Test public void testTintColor() { ReactImageManager viewManager = new ReactImageManager(); diff --git a/android/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java b/android/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java index f993878840a7d..b908654deb6d0 100644 --- a/android/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java +++ b/android/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.java @@ -221,6 +221,7 @@ public void testNumLines() { public void testKeyboardType() { ReactEditText view = mManager.createViewInstance(mThemedContext); int numberPadTypeFlags = InputType.TYPE_CLASS_NUMBER; + int urlTypeFlags = InputType.TYPE_TEXT_VARIATION_URI; int decimalPadTypeFlags = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL; int numericTypeFlags = InputType.TYPE_CLASS_NUMBER @@ -246,6 +247,9 @@ public void testKeyboardType() { mManager.updateProperties(view, buildStyles("keyboardType", "number-pad")); assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(numberPadTypeFlags); + mManager.updateProperties(view, buildStyles("keyboardType", "url")); + assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(urlTypeFlags); + mManager.updateProperties(view, buildStyles("keyboardType", "decimal-pad")); assertThat(view.getInputType() & generalKeyboardTypeFlags).isEqualTo(decimalPadTypeFlags); diff --git a/android/ReactAndroid/src/test/java/org/mockito/configuration/BUCK b/android/ReactAndroid/src/test/java/org/mockito/configuration/BUCK index a29174b85a037..ed96ae2d0653c 100644 --- a/android/ReactAndroid/src/test/java/org/mockito/configuration/BUCK +++ b/android/ReactAndroid/src/test/java/org/mockito/configuration/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_android_toplevel_dep", rn_android_library( name = "configuration", srcs = glob(["**/*.java"]), + autoglob = False, visibility = [ "PUBLIC", ], diff --git a/android/ReactCommon/React-Fabric.podspec b/android/ReactCommon/React-Fabric.podspec index 5ffcdd2153741..742040c57d40d 100644 --- a/android/ReactCommon/React-Fabric.podspec +++ b/android/ReactCommon/React-Fabric.podspec @@ -11,15 +11,16 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' folly_dep_name = 'RCT-Folly/Fabric' boost_compiler_flags = '-Wno-documentation' +react_native_path = ".." Pod::Spec.new do |s| s.name = "React-Fabric" @@ -28,12 +29,11 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "dummyFile.cpp" - s.library = "stdc++" s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", - "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" } + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" } s.dependency folly_dep_name, folly_version s.dependency "React-graphics", version @@ -82,7 +82,7 @@ Pod::Spec.new do |s| ss.source_files = "react/renderer/core/**/*.{m,mm,cpp,h}" ss.exclude_files = "react/renderer/core/tests" ss.header_dir = "react/renderer/core" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end s.subspec "componentregistry" do |ss| @@ -93,6 +93,14 @@ Pod::Spec.new do |s| ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end + s.subspec "componentregistrynative" do |ss| + ss.dependency folly_dep_name, folly_version + ss.compiler_flags = folly_compiler_flags + ss.source_files = "react/renderer/componentregistry/native/**/*.{m,mm,cpp,h}" + ss.header_dir = "react/renderer/componentregistry/native" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + end + s.subspec "components" do |ss| ss.subspec "activityindicator" do |sss| sss.dependency folly_dep_name, folly_version @@ -139,15 +147,6 @@ Pod::Spec.new do |s| sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end - ss.subspec "picker" do |sss| - sss.dependency folly_dep_name, folly_version - sss.compiler_flags = folly_compiler_flags - sss.source_files = "react/renderer/components/picker/iospicker/**/*.{m,mm,cpp,h}" - sss.exclude_files = "react/renderer/components/picker/iospicker/tests" - sss.header_dir = "react/renderer/components/iospicker" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } - end - ss.subspec "rncore" do |sss| sss.dependency folly_dep_name, folly_version sss.compiler_flags = folly_compiler_flags @@ -232,7 +231,16 @@ Pod::Spec.new do |s| end end - s.subspec "debug" do |ss| + s.subspec "debug_core" do |ss| + ss.dependency folly_dep_name, folly_version + ss.compiler_flags = folly_compiler_flags + ss.source_files = "react/debug/**/*.{m,mm,cpp,h}" + ss.exclude_files = "react/debug/tests" + ss.header_dir = "react/debug" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + end + + s.subspec "debug_renderer" do |ss| ss.dependency folly_dep_name, folly_version ss.compiler_flags = folly_compiler_flags ss.source_files = "react/renderer/debug/**/*.{m,mm,cpp,h}" @@ -301,9 +309,44 @@ Pod::Spec.new do |s| ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end + s.subspec "telemetry" do |ss| + ss.dependency folly_dep_name, folly_version + ss.compiler_flags = folly_compiler_flags + ss.source_files = "react/renderer/telemetry/**/*.{m,mm,cpp,h}" + ss.exclude_files = "react/renderer/telemetry/tests" + ss.header_dir = "react/renderer/telemetry" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + end + + s.subspec "leakchecker" do |ss| + ss.dependency folly_dep_name, folly_version + ss.compiler_flags = folly_compiler_flags + ss.source_files = "react/renderer/leakchecker/**/*.{cpp,h}" + ss.exclude_files = "react/renderer/leakchecker/tests" + ss.header_dir = "react/renderer/leakchecker" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + end + + s.subspec "runtimescheduler" do |ss| + ss.dependency folly_dep_name, folly_version + ss.compiler_flags = folly_compiler_flags + ss.source_files = "react/renderer/runtimescheduler/**/*.{cpp,h}" + ss.exclude_files = "react/renderer/runtimescheduler/tests" + ss.header_dir = "react/renderer/runtimescheduler" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + end + s.subspec "utils" do |ss| ss.source_files = "react/utils/*.{m,mm,cpp,h}" ss.header_dir = "react/utils" ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end + + use_react_native_codegen!(s, { + :react_native_path => react_native_path, + :js_srcs_dir => "#{react_native_path}/Libraries", + :library_name => "rncore", + :library_type => "components", + :output_dir => "#{react_native_path}/ReactCommon", + }) end diff --git a/android/ReactCommon/ReactCommon.podspec b/android/ReactCommon/ReactCommon.podspec index c49fbd8c2c545..a9176cbb5b807 100644 --- a/android/ReactCommon/ReactCommon.podspec +++ b/android/ReactCommon/ReactCommon.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -28,11 +28,11 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.header_dir = "ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" } @@ -45,6 +45,7 @@ Pod::Spec.new do |s| ss.dependency "React-cxxreact", version ss.dependency "React-jsi", version ss.dependency "RCT-Folly", folly_version + s.dependency "React-logger", version ss.dependency "DoubleConversion" ss.dependency "glog" @@ -53,6 +54,10 @@ Pod::Spec.new do |s| "react/nativemodule/core/platform/ios/**/*.{mm,cpp,h}" end + s.subspec "react_debug_core" do |sss| + sss.source_files = "react/debug/*.{cpp,h}" + end + ss.subspec "samples" do |sss| sss.source_files = "react/nativemodule/samples/ReactCommon/**/*.{cpp,h}", "react/nativemodule/samples/platform/ios/**/*.{mm,cpp,h}" diff --git a/android/ReactCommon/better/Android.mk b/android/ReactCommon/better/Android.mk index d21a67d8648e9..fed79b0fbbc13 100644 --- a/android/ReactCommon/better/Android.mk +++ b/android/ReactCommon/better/Android.mk @@ -17,7 +17,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Better\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := diff --git a/android/ReactCommon/better/BUCK b/android/ReactCommon/better/BUCK index 53358d1a55679..c287ec796522c 100644 --- a/android/ReactCommon/better/BUCK +++ b/android/ReactCommon/better/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -6,6 +5,7 @@ load( "CXX", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "rn_xplat_cxx_library", "subdir_glob", ) @@ -29,12 +29,6 @@ rn_xplat_cxx_library( ], prefix = "better", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, diff --git a/android/ReactCommon/callinvoker/Android.mk b/android/ReactCommon/callinvoker/Android.mk index 040d5d8ef634b..3cef3d750c875 100644 --- a/android/ReactCommon/callinvoker/Android.mk +++ b/android/ReactCommon/callinvoker/Android.mk @@ -13,7 +13,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon # Header search path for modules that depend on this module LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall # Name of this module. LOCAL_MODULE := callinvoker diff --git a/android/ReactCommon/callinvoker/BUCK b/android/ReactCommon/callinvoker/BUCK index b050d1f025424..d6ff5cdb39a49 100644 --- a/android/ReactCommon/callinvoker/BUCK +++ b/android/ReactCommon/callinvoker/BUCK @@ -1,4 +1,4 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "rn_xplat_cxx_library", "subdir_glob") +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "CXX", "rn_xplat_cxx_library", "subdir_glob") rn_xplat_cxx_library( name = "callinvoker", @@ -10,14 +10,8 @@ rn_xplat_cxx_library( ], prefix = "ReactCommon", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], labels = ["supermodule:xplat/default/public.react_native.infra"], - platforms = (ANDROID, APPLE), + platforms = (ANDROID, APPLE, CXX), preferred_linkage = "static", preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/android/ReactCommon/callinvoker/React-callinvoker.podspec b/android/ReactCommon/callinvoker/React-callinvoker.podspec index 76aec146d61cd..b05fad87c432b 100644 --- a/android/ReactCommon/callinvoker/React-callinvoker.podspec +++ b/android/ReactCommon/callinvoker/React-callinvoker.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "ReactCommon" diff --git a/android/ReactCommon/cxxreact/Android.mk b/android/ReactCommon/cxxreact/Android.mk index a5024364c6088..392c33b730a4a 100644 --- a/android/ReactCommon/cxxreact/Android.mk +++ b/android/ReactCommon/cxxreact/Android.mk @@ -20,7 +20,7 @@ LOCAL_CFLAGS := \ LOCAL_CFLAGS += -fexceptions -frtti -Wno-unused-lambda-capture LOCAL_STATIC_LIBRARIES := boost jsi callinvoker reactperflogger runtimeexecutor -LOCAL_SHARED_LIBRARIES := jsinspector libfolly_json glog +LOCAL_SHARED_LIBRARIES := jsinspector libfolly_json glog logger include $(BUILD_STATIC_LIBRARY) @@ -34,3 +34,4 @@ $(call import-module,jsi) $(call import-module,jsinspector) $(call import-module,hermes/inspector) $(call import-module,hermes/executor) +$(call import-module,logger) diff --git a/android/ReactCommon/cxxreact/BUCK b/android/ReactCommon/cxxreact/BUCK index 5e0d7af0a0f32..a662a78414ba7 100644 --- a/android/ReactCommon/cxxreact/BUCK +++ b/android/ReactCommon/cxxreact/BUCK @@ -1,11 +1,6 @@ load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob") load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "get_android_inspector_flags", "get_apple_compiler_flags", "get_apple_inspector_flags", "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library") -CXX_LIBRARY_COMPILER_FLAGS = [ - "-std=c++14", - "-Wall", -] - rn_xplat_cxx_library( name = "module", header_namespace = "", @@ -17,7 +12,6 @@ rn_xplat_cxx_library( ], prefix = "cxxreact", ), - compiler_flags = CXX_LIBRARY_COMPILER_FLAGS, fbobjc_compiler_flags = get_apple_compiler_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], @@ -39,16 +33,9 @@ rn_xplat_cxx_library( [("", "JSBigString.h")], prefix = "cxxreact", ), - compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [ - "-fexceptions", - "-frtti", - ], fbobjc_compiler_flags = get_apple_compiler_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], - preprocessor_flags = [ - "-DWITH_FBREMAP=1", - ], visibility = [ "PUBLIC", ], @@ -64,9 +51,8 @@ rn_xplat_cxx_library( srcs = ["SampleCxxModule.cpp"], header_namespace = "", exported_headers = ["SampleCxxModule.h"], - compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [ + compiler_flags = [ "-fno-omit-frame-pointer", - "-fexceptions", ], fbobjc_compiler_flags = get_apple_compiler_flags(), labels = ["supermodule:xplat/default/public.react_native.infra"], @@ -123,10 +109,6 @@ rn_xplat_cxx_library( ) for header in CXXREACT_PUBLIC_HEADERS ]), - compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [ - "-fexceptions", - "-frtti", - ], fbandroid_preprocessor_flags = get_android_inspector_flags(), fbobjc_compiler_flags = get_apple_compiler_flags(), fbobjc_force_static = True, @@ -155,6 +137,7 @@ rn_xplat_cxx_library( react_native_xplat_target("microprofiler:microprofiler"), react_native_xplat_target("runtimeexecutor:runtimeexecutor"), react_native_xplat_target("reactperflogger:reactperflogger"), + react_native_xplat_target("logger:logger"), "//third-party/glog:glog", "//xplat/folly:optional", ], diff --git a/android/ReactCommon/cxxreact/CxxNativeModule.cpp b/android/ReactCommon/cxxreact/CxxNativeModule.cpp index bfbb608553b52..0bcf13657eed9 100644 --- a/android/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/android/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -16,6 +16,8 @@ #include "MessageQueueThread.h" #include "SystraceSection.h" +#include + using facebook::xplat::module::CxxModule; namespace facebook { namespace react { @@ -54,6 +56,26 @@ CxxModule::Callback convertCallback( } // namespace +bool CxxNativeModule::shouldWarnOnUse_ = false; + +void CxxNativeModule::setShouldWarnOnUse(bool value) { + shouldWarnOnUse_ = value; +} + +void CxxNativeModule::emitWarnIfWarnOnUsage( + const std::string &method_name, + const std::string &module_name) { + if (shouldWarnOnUse_) { + std::string message = folly::to( + "Calling ", + method_name, + " on Cxx NativeModule (name = \"", + module_name, + "\")."); + react_native_log_warn(message.c_str()); + } +} + std::string CxxNativeModule::getName() { return name_; } @@ -87,6 +109,8 @@ folly::dynamic CxxNativeModule::getConstants() { return nullptr; } + emitWarnIfWarnOnUsage("getConstants()", getName()); + folly::dynamic constants = folly::dynamic::object(); for (auto &pair : module_->getConstants()) { constants.insert(std::move(pair.first), std::move(pair.second)); @@ -121,6 +145,8 @@ void CxxNativeModule::invoke( "Method ", method.name, " is synchronous but invoked asynchronously")); } + emitWarnIfWarnOnUsage(method.name, getName()); + if (params.size() < method.callbacks) { throw std::invalid_argument(folly::to( "Expected ", @@ -204,6 +230,8 @@ MethodCallResult CxxNativeModule::callSerializableNativeHook( "Method ", method.name, " is asynchronous but invoked synchronously")); } + emitWarnIfWarnOnUsage(method.name, getName()); + return method.syncFunc(std::move(args)); } diff --git a/android/ReactCommon/cxxreact/CxxNativeModule.h b/android/ReactCommon/cxxreact/CxxNativeModule.h index 9d730ff794247..a10f0ec5cb0a5 100644 --- a/android/ReactCommon/cxxreact/CxxNativeModule.h +++ b/android/ReactCommon/cxxreact/CxxNativeModule.h @@ -20,6 +20,8 @@ namespace react { class Instance; class MessageQueueThread; +typedef void (*WarnOnUsageLogger)(std::string message); + std::function makeCallback( std::weak_ptr instance, const folly::dynamic &callbackId); @@ -46,6 +48,8 @@ class RN_EXPORT CxxNativeModule : public NativeModule { unsigned int hookId, folly::dynamic &&args) override; + static void setShouldWarnOnUse(bool value); + private: void lazyInit(); @@ -55,6 +59,11 @@ class RN_EXPORT CxxNativeModule : public NativeModule { std::shared_ptr messageQueueThread_; std::unique_ptr module_; std::vector methods_; + void emitWarnIfWarnOnUsage( + const std::string &method_name, + const std::string &module_name); + + static bool shouldWarnOnUse_; }; } // namespace react diff --git a/android/ReactCommon/cxxreact/Instance.cpp b/android/ReactCommon/cxxreact/Instance.cpp index 2f2dc5d448d16..f1150821a9659 100644 --- a/android/ReactCommon/cxxreact/Instance.cpp +++ b/android/ReactCommon/cxxreact/Instance.cpp @@ -7,6 +7,7 @@ #include "Instance.h" +#include "ErrorUtils.h" #include "JSBigString.h" #include "JSBundleType.h" #include "JSExecutor.h" @@ -108,6 +109,18 @@ void Instance::loadScriptFromString( } } +bool Instance::isHBCBundle(const char *sourcePath) { + std::ifstream bundle_stream(sourcePath, std::ios_base::in); + BundleHeader header; + + if (!bundle_stream || + !bundle_stream.read(reinterpret_cast(&header), sizeof(header))) { + return false; + } + + return parseTypeFromHeader(header) == ScriptTag::HBCBundle; +} + bool Instance::isIndexedRAMBundle(const char *sourcePath) { std::ifstream bundle_stream(sourcePath, std::ios_base::in); BundleHeader header; @@ -234,7 +247,26 @@ std::shared_ptr Instance::getJSCallInvoker() { } RuntimeExecutor Instance::getRuntimeExecutor() { - return nativeToJsBridge_->getRuntimeExecutor(); + std::weak_ptr weakNativeToJsBridge = nativeToJsBridge_; + + auto runtimeExecutor = + [weakNativeToJsBridge]( + std::function &&callback) { + if (auto strongNativeToJsBridge = weakNativeToJsBridge.lock()) { + strongNativeToJsBridge->runOnExecutorQueue( + [callback = std::move(callback)](JSExecutor *executor) { + jsi::Runtime *runtime = + (jsi::Runtime *)executor->getJavaScriptContext(); + try { + callback(*runtime); + executor->flush(); + } catch (jsi::JSError &originalError) { + handleJSError(*runtime, originalError, true); + } + }); + } + }; + return runtimeExecutor; } std::shared_ptr Instance::getDecoratedNativeCallInvoker( diff --git a/android/ReactCommon/cxxreact/Instance.h b/android/ReactCommon/cxxreact/Instance.h index f475bf8a7fa72..7cd11f9bdf50b 100644 --- a/android/ReactCommon/cxxreact/Instance.h +++ b/android/ReactCommon/cxxreact/Instance.h @@ -56,6 +56,7 @@ class RN_EXPORT Instance { std::unique_ptr string, std::string sourceURL, bool loadSynchronously); + static bool isHBCBundle(const char *sourcePath); static bool isIndexedRAMBundle(const char *sourcePath); static bool isIndexedRAMBundle(std::unique_ptr *string); void loadRAMBundleFromString( diff --git a/android/ReactCommon/cxxreact/JSBundleType.cpp b/android/ReactCommon/cxxreact/JSBundleType.cpp index 05bacb4107c7b..f76610d461a98 100644 --- a/android/ReactCommon/cxxreact/JSBundleType.cpp +++ b/android/ReactCommon/cxxreact/JSBundleType.cpp @@ -13,11 +13,14 @@ namespace facebook { namespace react { static uint32_t constexpr RAMBundleMagicNumber = 0xFB0BD1E5; +static uint32_t constexpr HBCBundleMagicNumber = 0xffe7c3c3; ScriptTag parseTypeFromHeader(const BundleHeader &header) { switch (folly::Endian::little(header.magic)) { case RAMBundleMagicNumber: return ScriptTag::RAMBundle; + case HBCBundleMagicNumber: + return ScriptTag::HBCBundle; default: return ScriptTag::String; } @@ -29,6 +32,8 @@ const char *stringForScriptTag(const ScriptTag &tag) { return "String"; case ScriptTag::RAMBundle: return "RAM Bundle"; + case ScriptTag::HBCBundle: + return "HBC Bundle"; } return ""; } diff --git a/android/ReactCommon/cxxreact/JSBundleType.h b/android/ReactCommon/cxxreact/JSBundleType.h index 45a9045818897..be39711dd8d63 100644 --- a/android/ReactCommon/cxxreact/JSBundleType.h +++ b/android/ReactCommon/cxxreact/JSBundleType.h @@ -27,6 +27,7 @@ namespace react { enum struct ScriptTag { String = 0, RAMBundle, + HBCBundle, }; /** diff --git a/android/ReactCommon/cxxreact/JsArgumentHelpers-inl.h b/android/ReactCommon/cxxreact/JsArgumentHelpers-inl.h index 72c2d0d2f6b44..fa76074d0712e 100644 --- a/android/ReactCommon/cxxreact/JsArgumentHelpers-inl.h +++ b/android/ReactCommon/cxxreact/JsArgumentHelpers-inl.h @@ -14,7 +14,7 @@ namespace xplat { namespace detail { template -R jsArg1(const folly::dynamic &arg, M asFoo, const T &... desc) { +R jsArg1(const folly::dynamic &arg, M asFoo, const T &...desc) { try { return (arg.*asFoo)(); } catch (const folly::TypeError &ex) { @@ -35,7 +35,7 @@ template R jsArg( const folly::dynamic &arg, R (folly::dynamic::*asFoo)() const, - const T &... desc) { + const T &...desc) { return detail::jsArg1(arg, asFoo, desc...); } @@ -43,7 +43,7 @@ template R jsArg( const folly::dynamic &arg, R (folly::dynamic::*asFoo)() const &, - const T &... desc) { + const T &...desc) { return detail::jsArg1(arg, asFoo, desc...); } diff --git a/android/ReactCommon/cxxreact/JsArgumentHelpers.h b/android/ReactCommon/cxxreact/JsArgumentHelpers.h index 7e9f13934f6a1..25702ba845e5a 100644 --- a/android/ReactCommon/cxxreact/JsArgumentHelpers.h +++ b/android/ReactCommon/cxxreact/JsArgumentHelpers.h @@ -39,12 +39,12 @@ template R jsArg( const folly::dynamic &arg, R (folly::dynamic::*asFoo)() const, - const T &... desc); + const T &...desc); template R jsArg( const folly::dynamic &arg, R (folly::dynamic::*asFoo)() const &, - const T &... desc); + const T &...desc); // This is like jsArg, but a operates on a dynamic representing an array of // arguments. The argument n is used both to index the array and build the diff --git a/android/ReactCommon/cxxreact/MethodCall.cpp b/android/ReactCommon/cxxreact/MethodCall.cpp index f5f541265a5cb..3a22b8994b9c4 100644 --- a/android/ReactCommon/cxxreact/MethodCall.cpp +++ b/android/ReactCommon/cxxreact/MethodCall.cpp @@ -73,8 +73,8 @@ std::vector parseMethodCalls(folly::dynamic &&jsonData) { } methodCalls.emplace_back( - moduleIds[i].asInt(), - methodIds[i].asInt(), + static_cast(moduleIds[i].asInt()), + static_cast(methodIds[i].asInt()), std::move(params[i]), callId); diff --git a/android/ReactCommon/cxxreact/ModuleRegistry.cpp b/android/ReactCommon/cxxreact/ModuleRegistry.cpp index da75bc5b5857f..741d97073d36b 100644 --- a/android/ReactCommon/cxxreact/ModuleRegistry.cpp +++ b/android/ReactCommon/cxxreact/ModuleRegistry.cpp @@ -48,6 +48,11 @@ void ModuleRegistry::updateModuleNamesFromIndex(size_t index) { void ModuleRegistry::registerModules( std::vector> modules) { SystraceSection s_("ModuleRegistry::registerModules"); + // Noop if there are no NativeModules to add + if (modules.empty()) { + return; + } + if (modules_.empty() && unknownModules_.empty()) { modules_ = std::move(modules); } else { diff --git a/android/ReactCommon/cxxreact/NativeToJsBridge.cpp b/android/ReactCommon/cxxreact/NativeToJsBridge.cpp index d0c13554f28a1..3a695201057bc 100644 --- a/android/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/android/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -340,26 +340,5 @@ std::shared_ptr NativeToJsBridge::getDecoratedNativeCallInvoker( return std::make_shared(m_delegate, nativeInvoker); } -RuntimeExecutor NativeToJsBridge::getRuntimeExecutor() { - auto runtimeExecutor = - [this, isDestroyed = m_destroyed]( - std::function &&callback) { - if (*isDestroyed) { - return; - } - runOnExecutorQueue( - [callback = std::move(callback)](JSExecutor *executor) { - jsi::Runtime *runtime = - (jsi::Runtime *)executor->getJavaScriptContext(); - try { - callback(*runtime); - } catch (jsi::JSError &originalError) { - handleJSError(*runtime, originalError, true); - } - }); - }; - return runtimeExecutor; -} - } // namespace react } // namespace facebook diff --git a/android/ReactCommon/cxxreact/NativeToJsBridge.h b/android/ReactCommon/cxxreact/NativeToJsBridge.h index d298f9c27960c..f9548c59509db 100644 --- a/android/ReactCommon/cxxreact/NativeToJsBridge.h +++ b/android/ReactCommon/cxxreact/NativeToJsBridge.h @@ -107,12 +107,6 @@ class NativeToJsBridge { std::shared_ptr getDecoratedNativeCallInvoker( std::shared_ptr nativeInvoker); - /** - * RuntimeExecutor is used on Android to access the jsi::Runtime from Fabric - * and TurboModules - */ - RuntimeExecutor getRuntimeExecutor(); - private: // This is used to avoid a race condition where a proxyCallback gets queued // after ~NativeToJsBridge(), on the same thread. In that case, the callback diff --git a/android/ReactCommon/cxxreact/React-cxxreact.podspec b/android/ReactCommon/cxxreact/React-cxxreact.podspec index b31dfe6359fd7..14bd5eed723cf 100644 --- a/android/ReactCommon/cxxreact/React-cxxreact.podspec +++ b/android/ReactCommon/cxxreact/React-cxxreact.podspec @@ -12,13 +12,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -28,15 +28,15 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source s.source_files = "*.{cpp,h}" s.exclude_files = "SampleCxxModule.*" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "cxxreact" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" @@ -45,4 +45,5 @@ Pod::Spec.new do |s| s.dependency "React-runtimeexecutor", version s.dependency "React-perflogger", version s.dependency "React-jsi", version + s.dependency "React-logger", version end diff --git a/android/ReactCommon/cxxreact/ReactNativeVersion.h b/android/ReactCommon/cxxreact/ReactNativeVersion.h index 85449c0785785..f10f8271384c9 100644 --- a/android/ReactCommon/cxxreact/ReactNativeVersion.h +++ b/android/ReactCommon/cxxreact/ReactNativeVersion.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by scripts/bump-oss-version.js + * @generated by scripts/set-rn-version.js */ #pragma once @@ -16,7 +16,7 @@ namespace facebook::react { constexpr struct { int32_t Major = 0; - int32_t Minor = 64; + int32_t Minor = 67; int32_t Patch = 2; std::string_view Prerelease = ""; } ReactNativeVersion; diff --git a/android/ReactCommon/cxxreact/SystraceSection.h b/android/ReactCommon/cxxreact/SystraceSection.h index 97f93af0b60b9..3a59f0ed6118e 100644 --- a/android/ReactCommon/cxxreact/SystraceSection.h +++ b/android/ReactCommon/cxxreact/SystraceSection.h @@ -29,7 +29,7 @@ struct ConcreteSystraceSection { template explicit ConcreteSystraceSection( const char *name, - ConvertsToStringPiece &&... args) + ConvertsToStringPiece &&...args) : m_section(TRACE_TAG_REACT_CXX_BRIDGE, name, args...) {} private: @@ -42,7 +42,7 @@ struct DummySystraceSection { template explicit DummySystraceSection( __unused const char *name, - __unused ConvertsToStringPiece &&... args) {} + __unused ConvertsToStringPiece &&...args) {} }; using SystraceSection = DummySystraceSection; #endif diff --git a/android/ReactCommon/cxxreact/re_worker_requirements b/android/ReactCommon/cxxreact/re_worker_requirements deleted file mode 100644 index 3b5fff6d2e136..0000000000000 --- a/android/ReactCommon/cxxreact/re_worker_requirements +++ /dev/null @@ -1,6 +0,0 @@ -{ - "bridgeAndroid#android-x86,merge_structure_97d5739e": { - "workerSize": "MEDIUM", - "platformType": "LINUX" - } -} \ No newline at end of file diff --git a/android/ReactCommon/hermes/React-hermes.podspec b/android/ReactCommon/hermes/React-hermes.podspec new file mode 100644 index 0000000000000..50eaa2ad3fb37 --- /dev/null +++ b/android/ReactCommon/hermes/React-hermes.podspec @@ -0,0 +1,53 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -Wno-comma -Wno-shorten-64-to-32' +folly_version = '2021.06.28.00-v2' +boost_compiler_flags = '-Wno-documentation' + +Pod::Spec.new do |s| + s.name = "React-hermes" + s.version = version + s.summary = "-" # TODO + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Facebook, Inc. and its affiliates" + s.platforms = { :osx => "10.14", :ios => "11.0" } + s.source = source + s.source_files = "executor/*.{cpp,h}", + "inspector/*.{cpp,h}", + "inspector/chrome/*.{cpp,h}", + "inspector/detail/*.{cpp,h}" + s.public_header_files = "executor/HermesExecutorFactory.h" + s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/..\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/libevent/include\"", + "GCC_PREPROCESSOR_DEFINITIONS" => "HERMES_ENABLE_DEBUGGER=1", + } + s.header_dir = "reacthermes" + s.dependency "React-cxxreact", version + s.dependency "React-jsi", version + s.dependency "React-jsiexecutor", version + s.dependency "React-jsinspector", version + s.dependency "React-perflogger", version + s.dependency "RCT-Folly", folly_version + s.dependency "DoubleConversion" + s.dependency "glog" + s.dependency "RCT-Folly/Futures", folly_version + s.dependency "hermes-engine" +end diff --git a/android/ReactCommon/hermes/executor/Android.mk b/android/ReactCommon/hermes/executor/Android.mk index 491100c33a99e..52b4d25085f74 100644 --- a/android/ReactCommon/hermes/executor/Android.mk +++ b/android/ReactCommon/hermes/executor/Android.mk @@ -16,8 +16,8 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-node-module,$(LOCAL_PATH),hermes-engine)/android/include LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_STATIC_LIBRARIES := libjsi libjsireact -LOCAL_SHARED_LIBRARIES := libhermes +LOCAL_STATIC_LIBRARIES := libjsireact +LOCAL_SHARED_LIBRARIES := libhermes libjsi include $(BUILD_SHARED_LIBRARY) @@ -31,7 +31,7 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-node-module,$(LOCAL_PATH),hermes-engine)/android/include LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_STATIC_LIBRARIES := libjsi libjsireact libhermes-inspector -LOCAL_SHARED_LIBRARIES := libhermes +LOCAL_STATIC_LIBRARIES := libjsireact libhermes-inspector +LOCAL_SHARED_LIBRARIES := libhermes libjsi include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactCommon/hermes/inspector/Android.mk b/android/ReactCommon/hermes/inspector/Android.mk index a6692943e17de..461b10aaac332 100644 --- a/android/ReactCommon/hermes/inspector/Android.mk +++ b/android/ReactCommon/hermes/inspector/Android.mk @@ -21,7 +21,6 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_ROOT) LOCAL_CPP_FEATURES := exceptions -LOCAL_STATIC_LIBRARIES := libjsi -LOCAL_SHARED_LIBRARIES := jsinspector libfb libfbjni libfolly_futures libfolly_json libhermes +LOCAL_SHARED_LIBRARIES := jsinspector libfb libfbjni libfolly_futures libfolly_json libhermes libjsi libglog include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactCommon/hermes/inspector/BUCK b/android/ReactCommon/hermes/inspector/BUCK index f3ea76a314efc..0ff039ceb1e37 100644 --- a/android/ReactCommon/hermes/inspector/BUCK +++ b/android/ReactCommon/hermes/inspector/BUCK @@ -3,7 +3,7 @@ load("@fbsource//tools/build_defs:fb_xplat_cxx_binary.bzl", "fb_xplat_cxx_binary") load("@fbsource//tools/build_defs:fb_xplat_cxx_library.bzl", "fb_xplat_cxx_library") load("@fbsource//tools/build_defs:fb_xplat_cxx_test.bzl", "fb_xplat_cxx_test") -load("@fbsource//tools/build_defs:platform_defs.bzl", "APPLE", "CXX") +load("@fbsource//tools/build_defs:platform_defs.bzl", "ANDROID", "APPLE", "CXX", "FBCODE", "WINDOWS") load("@fbsource//tools/build_defs/oss:rn_defs.bzl", "react_native_xplat_target") load("@fbsource//xplat/hermes/defs:hermes.bzl", "hermes_build_mode", "hermes_optimize_flag") @@ -53,6 +53,7 @@ fb_xplat_cxx_library( fbobjc_header_path_prefix = "hermes/inspector/chrome", labels = ["supermodule:xplat/default/public.hermes"], macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), tests = [":chrome-tests"], visibility = [ "PUBLIC", @@ -64,7 +65,6 @@ fb_xplat_cxx_library( ":inspectorlib", "//third-party/glog:glog", "//xplat/folly:futures", - "//xplat/folly:molly", "//xplat/hermes/API:HermesAPI", "//xplat/jsi:JSIDynamic", "//xplat/jsi:jsi", @@ -83,6 +83,7 @@ fb_xplat_cxx_test( cxx_deps = [react_native_xplat_target("jsinspector:jsinspector")], fbandroid_deps = [react_native_xplat_target("jsinspector:jsinspector")], fbobjc_deps = [react_native_xplat_target("jsinspector:jsinspector")], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), visibility = [ "PUBLIC", ], @@ -115,18 +116,19 @@ fb_xplat_cxx_library( # no JNI_Onload. fbandroid_allow_jni_merging = False, fbandroid_deps = [ - "//fbandroid/native/fb:fb", + "//fbandroid/libraries/fbjni:fbjni", ], fbobjc_header_path_prefix = "hermes/inspector/detail", labels = ["supermodule:xplat/default/public.hermes"], macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), tests = [":detail-tests"], visibility = [ "PUBLIC", ], xcode_public_headers_symlinks = True, deps = [ - "//xplat/folly:molly", + "//xplat/folly:executor", ], ) @@ -139,6 +141,7 @@ fb_xplat_cxx_test( "detail/tests/*.h", ]), compiler_flags = CFLAGS_BY_MODE[hermes_build_mode()], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), visibility = [ "PUBLIC", ], @@ -193,6 +196,7 @@ fb_xplat_cxx_library( fbobjc_header_path_prefix = "hermes/inspector", labels = ["supermodule:xplat/default/public.hermes"], macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), visibility = [ "PUBLIC", ], @@ -201,7 +205,6 @@ fb_xplat_cxx_library( ":detail", "//third-party/glog:glog", "//xplat/folly:futures", - "//xplat/folly:molly", "//xplat/hermes/API:HermesAPI", "//xplat/jsi:jsi", ], diff --git a/android/ReactCommon/hermes/inspector/Inspector.cpp b/android/ReactCommon/hermes/inspector/Inspector.cpp index 21b7bd2108de1..1b5a09d356355 100644 --- a/android/ReactCommon/hermes/inspector/Inspector.cpp +++ b/android/ReactCommon/hermes/inspector/Inspector.cpp @@ -85,7 +85,7 @@ namespace debugger = ::facebook::hermes::debugger; */ // TODO: read this out of an env variable or config -static constexpr bool kShouldLog = true; +static constexpr bool kShouldLog = false; // Logging state transitions is done outside of transition() in a macro so that // function and line numbers in the log will be accurate. @@ -132,7 +132,6 @@ Inspector::Inspector( } Inspector::~Inspector() { - // TODO: think about expected detach flow debugger_.setEventObserver(nullptr); } @@ -186,7 +185,7 @@ void Inspector::installConsoleFunction( auto obj = val.getObject(runtime); if (obj.isFunction(runtime)) { auto func = obj.getFunction(runtime); - func.call(runtime, args, count); + func.callWithThis(runtime, *originalConsole, args, count); } } } diff --git a/android/ReactCommon/hermes/inspector/Inspector.h b/android/ReactCommon/hermes/inspector/Inspector.h index 3e4612530ea86..a55f1c45b9cfb 100644 --- a/android/ReactCommon/hermes/inspector/Inspector.h +++ b/android/ReactCommon/hermes/inspector/Inspector.h @@ -319,11 +319,6 @@ class Inspector : public facebook::hermes::debugger::EventObserver, facebook::hermes::debugger::Debugger &debugger_; InspectorObserver &observer_; - // All client methods (e.g. enable, setBreakpoint, resume, etc.) are executed - // on executor_ to prevent deadlocking on mutex_. See the implementation for - // more comments on the threading invariants used in this class. - std::unique_ptr executor_; - // All of the following member variables are guarded by mutex_. std::mutex mutex_; std::unique_ptr state_; @@ -360,6 +355,12 @@ class Inspector : public facebook::hermes::debugger::EventObserver, // Are we currently waiting for a debugger to attach, because we // requested 'pauseOnFirstStatement'? bool awaitingDebuggerOnStart_; + + // All client methods (e.g. enable, setBreakpoint, resume, etc.) are executed + // on executor_ to prevent deadlocking on mutex_. See the implementation for + // more comments on the threading invariants used in this class. + // NOTE: This needs to be declared LAST because it should be destroyed FIRST. + std::unique_ptr executor_; }; } // namespace inspector diff --git a/android/ReactCommon/hermes/inspector/InspectorState.cpp b/android/ReactCommon/hermes/inspector/InspectorState.cpp index 07e005ede5b49..a52e2ac25f0bb 100644 --- a/android/ReactCommon/hermes/inspector/InspectorState.cpp +++ b/android/ReactCommon/hermes/inspector/InspectorState.cpp @@ -299,9 +299,10 @@ void InspectorState::Running::pushPendingEval( std::shared_ptr> promise, folly::Function resultTransformer) { - PendingEval pendingEval{debugger::Command::eval(src, frameIndex), - promise, - std::move(resultTransformer)}; + PendingEval pendingEval{ + debugger::Command::eval(src, frameIndex), + promise, + std::move(resultTransformer)}; pendingEvals_.emplace(std::move(pendingEval)); @@ -476,9 +477,10 @@ void InspectorState::Paused::pushPendingEval( return; } - PendingEval pendingEval{debugger::Command::eval(src, frameIndex), - promise, - std::move(resultTransformer)}; + PendingEval pendingEval{ + debugger::Command::eval(src, frameIndex), + promise, + std::move(resultTransformer)}; pendingEvals_.emplace(std::move(pendingEval)); hasPendingWork_.notify_one(); } diff --git a/android/ReactCommon/hermes/inspector/chrome/Connection.cpp b/android/ReactCommon/hermes/inspector/chrome/Connection.cpp index 8af32ff530f1c..572a07cc2826f 100644 --- a/android/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/android/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -93,7 +94,12 @@ class Connection::Impl : public inspector::InspectorObserver, const m::heapProfiler::StartTrackingHeapObjectsRequest &req) override; void handle( const m::heapProfiler::StopTrackingHeapObjectsRequest &req) override; + void handle(const m::heapProfiler::StartSamplingRequest &req) override; + void handle(const m::heapProfiler::StopSamplingRequest &req) override; void handle(const m::heapProfiler::CollectGarbageRequest &req) override; + void handle( + const m::heapProfiler::GetObjectByHeapObjectIdRequest &req) override; + void handle(const m::heapProfiler::GetHeapObjectIdRequest &req) override; void handle(const m::runtime::EvaluateRequest &req) override; void handle(const m::runtime::GetPropertiesRequest &req) override; void handle(const m::runtime::RunIfWaitingForDebuggerRequest &req) override; @@ -580,6 +586,45 @@ void Connection::Impl::handle( /* stopStackTraceCapture */ true); } +void Connection::Impl::handle( + const m::heapProfiler::StartSamplingRequest &req) { + const auto id = req.id; + // This is the same default sampling interval that Chrome uses. + // https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#method-startSampling + constexpr size_t kDefaultSamplingInterval = 1 << 15; + const size_t samplingInterval = + req.samplingInterval.value_or(kDefaultSamplingInterval); + + inspector_ + ->executeIfEnabled( + "HeapProfiler.startSampling", + [this, samplingInterval](const debugger::ProgramState &) { + getRuntime().instrumentation().startHeapSampling(samplingInterval); + }) + .via(executor_.get()) + .thenValue( + [this, id](auto &&) { sendResponseToClient(m::makeOkResponse(id)); }) + .thenError(sendErrorToClient(req.id)); +} + +void Connection::Impl::handle(const m::heapProfiler::StopSamplingRequest &req) { + inspector_ + ->executeIfEnabled( + "HeapProfiler.stopSampling", + [this, id = req.id](const debugger::ProgramState &) { + std::ostringstream stream; + getRuntime().instrumentation().stopHeapSampling(stream); + folly::dynamic json = folly::parseJson(stream.str()); + m::heapProfiler::StopSamplingResponse resp; + resp.id = id; + m::heapProfiler::SamplingHeapProfile profile{json}; + resp.profile = profile; + sendResponseToClient(resp); + }) + .via(executor_.get()) + .thenError(sendErrorToClient(req.id)); +} + void Connection::Impl::handle( const m::heapProfiler::CollectGarbageRequest &req) { const auto id = req.id; @@ -596,6 +641,76 @@ void Connection::Impl::handle( .thenError(sendErrorToClient(req.id)); } +void Connection::Impl::handle( + const m::heapProfiler::GetObjectByHeapObjectIdRequest &req) { + uint64_t objID = atoi(req.objectId.c_str()); + folly::Optional group = req.objectGroup; + auto remoteObjPtr = std::make_shared(); + + inspector_ + ->executeIfEnabled( + "HeapProfiler.getObjectByHeapObjectId", + [this, remoteObjPtr, objID, group](const debugger::ProgramState &) { + jsi::Runtime *rt = &getRuntime(); + if (auto *hermesRT = dynamic_cast(rt)) { + jsi::Value val = hermesRT->getObjectForID(objID); + if (val.isNull()) { + return; + } + *remoteObjPtr = m::runtime::makeRemoteObject( + getRuntime(), val, objTable_, group.value_or("")); + } + }) + .via(executor_.get()) + .thenValue([this, id = req.id, remoteObjPtr](auto &&) { + if (!remoteObjPtr->type.empty()) { + m::heapProfiler::GetObjectByHeapObjectIdResponse resp; + resp.id = id; + resp.result = *remoteObjPtr; + sendResponseToClient(resp); + } else { + sendResponseToClient(m::makeErrorResponse( + id, m::ErrorCode::ServerError, "Object is not available")); + } + }) + .thenError(sendErrorToClient(req.id)); +} + +void Connection::Impl::handle( + const m::heapProfiler::GetHeapObjectIdRequest &req) { + // Use a shared_ptr because the stack frame will go away. + std::shared_ptr snapshotID = std::make_shared(0); + + inspector_ + ->executeIfEnabled( + "HeapProfiler.getHeapObjectId", + [this, req, snapshotID](const debugger::ProgramState &) { + if (const jsi::Value *valuePtr = objTable_.getValue(req.objectId)) { + jsi::Runtime *rt = &getRuntime(); + if (auto *hermesRT = dynamic_cast(rt)) { + *snapshotID = hermesRT->getUniqueID(*valuePtr); + } + } + }) + .via(executor_.get()) + .thenValue([this, id = req.id, snapshotID](auto &&) { + if (*snapshotID) { + m::heapProfiler::GetHeapObjectIdResponse resp; + resp.id = id; + // std::to_string is not available on Android, use a std::ostream + // instead. + std::ostringstream stream; + stream << *snapshotID; + resp.heapSnapshotObjectId = stream.str(); + sendResponseToClient(resp); + } else { + sendResponseToClient(m::makeErrorResponse( + id, m::ErrorCode::ServerError, "Object is not available")); + } + }) + .thenError(sendErrorToClient(req.id)); +} + void Connection::Impl::handle(const m::runtime::EvaluateRequest &req) { auto remoteObjPtr = std::make_shared(); diff --git a/android/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/android/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index 2a71a05640d4c..484719e130e7b 100644 --- a/android/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/android/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<> +// @generated SignedSource<<522f29c54f207a4f7b5c33af07cf64d0>> #include "MessageTypes.h" @@ -46,8 +46,16 @@ std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { {"Debugger.stepOver", makeUnique}, {"HeapProfiler.collectGarbage", makeUnique}, + {"HeapProfiler.getHeapObjectId", + makeUnique}, + {"HeapProfiler.getObjectByHeapObjectId", + makeUnique}, + {"HeapProfiler.startSampling", + makeUnique}, {"HeapProfiler.startTrackingHeapObjects", makeUnique}, + {"HeapProfiler.stopSampling", + makeUnique}, {"HeapProfiler.stopTrackingHeapObjects", makeUnique}, {"HeapProfiler.takeHeapSnapshot", @@ -219,6 +227,53 @@ dynamic debugger::CallFrame::toDynamic() const { return obj; } +heapProfiler::SamplingHeapProfileNode::SamplingHeapProfileNode( + const dynamic &obj) { + assign(callFrame, obj, "callFrame"); + assign(selfSize, obj, "selfSize"); + assign(id, obj, "id"); + assign(children, obj, "children"); +} + +dynamic heapProfiler::SamplingHeapProfileNode::toDynamic() const { + dynamic obj = dynamic::object; + + put(obj, "callFrame", callFrame); + put(obj, "selfSize", selfSize); + put(obj, "id", id); + put(obj, "children", children); + return obj; +} + +heapProfiler::SamplingHeapProfileSample::SamplingHeapProfileSample( + const dynamic &obj) { + assign(size, obj, "size"); + assign(nodeId, obj, "nodeId"); + assign(ordinal, obj, "ordinal"); +} + +dynamic heapProfiler::SamplingHeapProfileSample::toDynamic() const { + dynamic obj = dynamic::object; + + put(obj, "size", size); + put(obj, "nodeId", nodeId); + put(obj, "ordinal", ordinal); + return obj; +} + +heapProfiler::SamplingHeapProfile::SamplingHeapProfile(const dynamic &obj) { + assign(head, obj, "head"); + assign(samples, obj, "samples"); +} + +dynamic heapProfiler::SamplingHeapProfile::toDynamic() const { + dynamic obj = dynamic::object; + + put(obj, "head", head); + put(obj, "samples", samples); + return obj; +} + runtime::ExecutionContextDescription::ExecutionContextDescription( const dynamic &obj) { assign(id, obj, "id"); @@ -679,6 +734,92 @@ void heapProfiler::CollectGarbageRequest::accept( handler.handle(*this); } +heapProfiler::GetHeapObjectIdRequest::GetHeapObjectIdRequest() + : Request("HeapProfiler.getHeapObjectId") {} + +heapProfiler::GetHeapObjectIdRequest::GetHeapObjectIdRequest(const dynamic &obj) + : Request("HeapProfiler.getHeapObjectId") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(objectId, params, "objectId"); +} + +dynamic heapProfiler::GetHeapObjectIdRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "objectId", objectId); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void heapProfiler::GetHeapObjectIdRequest::accept( + RequestHandler &handler) const { + handler.handle(*this); +} + +heapProfiler::GetObjectByHeapObjectIdRequest::GetObjectByHeapObjectIdRequest() + : Request("HeapProfiler.getObjectByHeapObjectId") {} + +heapProfiler::GetObjectByHeapObjectIdRequest::GetObjectByHeapObjectIdRequest( + const dynamic &obj) + : Request("HeapProfiler.getObjectByHeapObjectId") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(objectId, params, "objectId"); + assign(objectGroup, params, "objectGroup"); +} + +dynamic heapProfiler::GetObjectByHeapObjectIdRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "objectId", objectId); + put(params, "objectGroup", objectGroup); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void heapProfiler::GetObjectByHeapObjectIdRequest::accept( + RequestHandler &handler) const { + handler.handle(*this); +} + +heapProfiler::StartSamplingRequest::StartSamplingRequest() + : Request("HeapProfiler.startSampling") {} + +heapProfiler::StartSamplingRequest::StartSamplingRequest(const dynamic &obj) + : Request("HeapProfiler.startSampling") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(samplingInterval, params, "samplingInterval"); +} + +dynamic heapProfiler::StartSamplingRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "samplingInterval", samplingInterval); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void heapProfiler::StartSamplingRequest::accept(RequestHandler &handler) const { + handler.handle(*this); +} + heapProfiler::StartTrackingHeapObjectsRequest::StartTrackingHeapObjectsRequest() : Request("HeapProfiler.startTrackingHeapObjects") {} @@ -708,6 +849,26 @@ void heapProfiler::StartTrackingHeapObjectsRequest::accept( handler.handle(*this); } +heapProfiler::StopSamplingRequest::StopSamplingRequest() + : Request("HeapProfiler.stopSampling") {} + +heapProfiler::StopSamplingRequest::StopSamplingRequest(const dynamic &obj) + : Request("HeapProfiler.stopSampling") { + assign(id, obj, "id"); + assign(method, obj, "method"); +} + +dynamic heapProfiler::StopSamplingRequest::toDynamic() const { + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + return obj; +} + +void heapProfiler::StopSamplingRequest::accept(RequestHandler &handler) const { + handler.handle(*this); +} + heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest() : Request("HeapProfiler.stopTrackingHeapObjects") {} @@ -973,6 +1134,59 @@ dynamic debugger::SetInstrumentationBreakpointResponse::toDynamic() const { return obj; } +heapProfiler::GetHeapObjectIdResponse::GetHeapObjectIdResponse( + const dynamic &obj) { + assign(id, obj, "id"); + + dynamic res = obj.at("result"); + assign(heapSnapshotObjectId, res, "heapSnapshotObjectId"); +} + +dynamic heapProfiler::GetHeapObjectIdResponse::toDynamic() const { + dynamic res = dynamic::object; + put(res, "heapSnapshotObjectId", heapSnapshotObjectId); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "result", std::move(res)); + return obj; +} + +heapProfiler::GetObjectByHeapObjectIdResponse::GetObjectByHeapObjectIdResponse( + const dynamic &obj) { + assign(id, obj, "id"); + + dynamic res = obj.at("result"); + assign(result, res, "result"); +} + +dynamic heapProfiler::GetObjectByHeapObjectIdResponse::toDynamic() const { + dynamic res = dynamic::object; + put(res, "result", result); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "result", std::move(res)); + return obj; +} + +heapProfiler::StopSamplingResponse::StopSamplingResponse(const dynamic &obj) { + assign(id, obj, "id"); + + dynamic res = obj.at("result"); + assign(profile, res, "profile"); +} + +dynamic heapProfiler::StopSamplingResponse::toDynamic() const { + dynamic res = dynamic::object; + put(res, "profile", profile); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "result", std::move(res)); + return obj; +} + runtime::EvaluateResponse::EvaluateResponse(const dynamic &obj) { assign(id, obj, "id"); diff --git a/android/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/android/ReactCommon/hermes/inspector/chrome/MessageTypes.h index 7778d88dc365d..2184a223588e4 100644 --- a/android/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/android/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<> +// @generated SignedSource<> #pragma once @@ -72,10 +72,21 @@ using UnserializableValue = std::string; namespace heapProfiler { struct AddHeapSnapshotChunkNotification; struct CollectGarbageRequest; +struct GetHeapObjectIdRequest; +struct GetHeapObjectIdResponse; +struct GetObjectByHeapObjectIdRequest; +struct GetObjectByHeapObjectIdResponse; +using HeapSnapshotObjectId = std::string; struct HeapStatsUpdateNotification; struct LastSeenObjectIdNotification; struct ReportHeapSnapshotProgressNotification; +struct SamplingHeapProfile; +struct SamplingHeapProfileNode; +struct SamplingHeapProfileSample; +struct StartSamplingRequest; struct StartTrackingHeapObjectsRequest; +struct StopSamplingRequest; +struct StopSamplingResponse; struct StopTrackingHeapObjectsRequest; struct TakeHeapSnapshotRequest; } // namespace heapProfiler @@ -101,8 +112,13 @@ struct RequestHandler { virtual void handle(const debugger::StepOutRequest &req) = 0; virtual void handle(const debugger::StepOverRequest &req) = 0; virtual void handle(const heapProfiler::CollectGarbageRequest &req) = 0; + virtual void handle(const heapProfiler::GetHeapObjectIdRequest &req) = 0; + virtual void handle( + const heapProfiler::GetObjectByHeapObjectIdRequest &req) = 0; + virtual void handle(const heapProfiler::StartSamplingRequest &req) = 0; virtual void handle( const heapProfiler::StartTrackingHeapObjectsRequest &req) = 0; + virtual void handle(const heapProfiler::StopSamplingRequest &req) = 0; virtual void handle( const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0; virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; @@ -130,8 +146,13 @@ struct NoopRequestHandler : public RequestHandler { void handle(const debugger::StepOutRequest &req) override {} void handle(const debugger::StepOverRequest &req) override {} void handle(const heapProfiler::CollectGarbageRequest &req) override {} + void handle(const heapProfiler::GetHeapObjectIdRequest &req) override {} + void handle( + const heapProfiler::GetObjectByHeapObjectIdRequest &req) override {} + void handle(const heapProfiler::StartSamplingRequest &req) override {} void handle( const heapProfiler::StartTrackingHeapObjectsRequest &req) override {} + void handle(const heapProfiler::StopSamplingRequest &req) override {} void handle( const heapProfiler::StopTrackingHeapObjectsRequest &req) override {} void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} @@ -230,6 +251,36 @@ struct debugger::CallFrame : public Serializable { folly::Optional returnValue; }; +struct heapProfiler::SamplingHeapProfileNode : public Serializable { + SamplingHeapProfileNode() = default; + explicit SamplingHeapProfileNode(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + runtime::CallFrame callFrame{}; + double selfSize{}; + int id{}; + std::vector children; +}; + +struct heapProfiler::SamplingHeapProfileSample : public Serializable { + SamplingHeapProfileSample() = default; + explicit SamplingHeapProfileSample(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + double size{}; + int nodeId{}; + double ordinal{}; +}; + +struct heapProfiler::SamplingHeapProfile : public Serializable { + SamplingHeapProfile() = default; + explicit SamplingHeapProfile(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + heapProfiler::SamplingHeapProfileNode head{}; + std::vector samples; +}; + struct runtime::ExecutionContextDescription : public Serializable { ExecutionContextDescription() = default; explicit ExecutionContextDescription(const folly::dynamic &obj); @@ -424,6 +475,37 @@ struct heapProfiler::CollectGarbageRequest : public Request { void accept(RequestHandler &handler) const override; }; +struct heapProfiler::GetHeapObjectIdRequest : public Request { + GetHeapObjectIdRequest(); + explicit GetHeapObjectIdRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + runtime::RemoteObjectId objectId{}; +}; + +struct heapProfiler::GetObjectByHeapObjectIdRequest : public Request { + GetObjectByHeapObjectIdRequest(); + explicit GetObjectByHeapObjectIdRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + heapProfiler::HeapSnapshotObjectId objectId{}; + folly::Optional objectGroup; +}; + +struct heapProfiler::StartSamplingRequest : public Request { + StartSamplingRequest(); + explicit StartSamplingRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + folly::Optional samplingInterval; +}; + struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { StartTrackingHeapObjectsRequest(); explicit StartTrackingHeapObjectsRequest(const folly::dynamic &obj); @@ -434,6 +516,14 @@ struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { folly::Optional trackAllocations; }; +struct heapProfiler::StopSamplingRequest : public Request { + StopSamplingRequest(); + explicit StopSamplingRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; +}; + struct heapProfiler::StopTrackingHeapObjectsRequest : public Request { StopTrackingHeapObjectsRequest(); explicit StopTrackingHeapObjectsRequest(const folly::dynamic &obj); @@ -544,6 +634,30 @@ struct debugger::SetInstrumentationBreakpointResponse : public Response { debugger::BreakpointId breakpointId{}; }; +struct heapProfiler::GetHeapObjectIdResponse : public Response { + GetHeapObjectIdResponse() = default; + explicit GetHeapObjectIdResponse(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + heapProfiler::HeapSnapshotObjectId heapSnapshotObjectId{}; +}; + +struct heapProfiler::GetObjectByHeapObjectIdResponse : public Response { + GetObjectByHeapObjectIdResponse() = default; + explicit GetObjectByHeapObjectIdResponse(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + runtime::RemoteObject result{}; +}; + +struct heapProfiler::StopSamplingResponse : public Response { + StopSamplingResponse() = default; + explicit StopSamplingResponse(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + heapProfiler::SamplingHeapProfile profile{}; +}; + struct runtime::EvaluateResponse : public Response { EvaluateResponse() = default; explicit EvaluateResponse(const folly::dynamic &obj); diff --git a/android/ReactCommon/hermes/inspector/chrome/cli/main.cpp b/android/ReactCommon/hermes/inspector/chrome/cli/main.cpp index 8a019b8baca5d..3e687be4f3617 100644 --- a/android/ReactCommon/hermes/inspector/chrome/cli/main.cpp +++ b/android/ReactCommon/hermes/inspector/chrome/cli/main.cpp @@ -156,8 +156,8 @@ static void sendResponse(const std::string &str) { static std::string readScriptSource(const char *path) { std::ifstream stream(path); - return std::string{std::istreambuf_iterator(stream), - std::istreambuf_iterator()}; + return std::string{ + std::istreambuf_iterator(stream), std::istreambuf_iterator()}; } static std::string getUrl(const char *path) { @@ -228,9 +228,10 @@ static void runScript(const std::string &scriptSource, const std::string &url) { int main(int argc, char **argv) { const char *shortOpts = "l:h"; - const option longOpts[] = {{"log", 1, nullptr, 'l'}, - {"help", 0, nullptr, 'h'}, - {nullptr, 0, nullptr, 0}}; + const option longOpts[] = { + {"log", 1, nullptr, 'l'}, + {"help", 0, nullptr, 'h'}, + {nullptr, 0, nullptr, 0}}; while (true) { int opt = getopt_long(argc, argv, shortOpts, longOpts, nullptr); diff --git a/android/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp b/android/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp index e4d857aac3f2b..f579799bbae53 100644 --- a/android/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp +++ b/android/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace facebook { namespace hermes { @@ -199,6 +200,12 @@ ResponseType send(SyncConnection &conn, int id) { return expectResponse(conn, id); } +template +ResponseType send(SyncConnection &conn, RequestType req) { + conn.send(req.toJson()); + return expectResponse(conn, req.id); +} + void sendRuntimeEvalRequest( SyncConnection &conn, int id, @@ -2449,6 +2456,143 @@ TEST(ConnectionTests, runIfWaitingForDebugger) { expectNotification(conn); } +TEST(ConnectionTests, heapProfilerSampling) { + TestContext context; + AsyncHermesRuntime &asyncRuntime = context.runtime(); + SyncConnection &conn = context.conn(); + int msgId = 1; + + send(conn, msgId++); + expectExecutionContextCreated(conn); + + asyncRuntime.executeScriptAsync(R"( + debugger; + function allocator() { + // Do some allocation. + return new Object; + } + (function main() { + var a = []; + for (var i = 0; i < 100; i++) { + a[i] = allocator(); + } + })(); + debugger; + )"); + expectNotification(conn); + + // We should get a pause before the first statement. + expectNotification(conn); + + { + m::heapProfiler::StartSamplingRequest req; + req.id = msgId++; + // Sample every 256 bytes to ensure there are some samples. The default is + // 32768, which is too high for a small example. Note that sampling is a + // random process, so there's no guarantee there will be any samples in any + // finite number of allocations. In practice the likelihood is so high that + // there shouldn't be any issues. + req.samplingInterval = 256; + send(conn, req); + } + // Resume, run the allocations, and once it's paused again, stop them. + send(conn, msgId++); + expectNotification(conn); + expectNotification(conn); + // Send the stop sampling request, expect the value coming back to be JSON. + auto resp = send< + m::heapProfiler::StopSamplingRequest, + m::heapProfiler::StopSamplingResponse>(conn, msgId++); + // Make sure there were some samples. + EXPECT_NE(resp.profile.samples.size(), 0); + // Don't test the content of the JSON, that is tested via the + // SamplingHeapProfilerTest. + + // Resume and exit + send(conn, msgId++); + expectNotification(conn); +} + +TEST(ConnectionTests, heapSnapshotRemoteObject) { + TestContext context; + AsyncHermesRuntime &asyncRuntime = context.runtime(); + std::shared_ptr runtime = asyncRuntime.runtime(); + SyncConnection &conn = context.conn(); + int msgId = 1; + + send(conn, msgId++); + expectExecutionContextCreated(conn); + + asyncRuntime.executeScriptAsync(R"( + storeValue([1, 2, 3]); + debugger; + )"); + expectNotification(conn); + + // We should get a pause before the first statement. + expectNotification(conn); + + { + // Take a heap snapshot first to assign IDs. + m::heapProfiler::TakeHeapSnapshotRequest req; + req.id = msgId++; + req.reportProgress = false; + // We don't need the response because we can directly query for object IDs + // from the runtime. + send(conn, req); + } + + const uint64_t globalObjID = runtime->getUniqueID(runtime->global()); + jsi::Value storedValue = asyncRuntime.awaitStoredValue(); + const uint64_t storedObjID = + runtime->getUniqueID(storedValue.asObject(*runtime)); + + auto testObject = [&msgId, &conn]( + uint64_t objID, + const char *type, + const char *className, + const char *description, + const char *subtype) { + // Get the object by its snapshot ID. + m::heapProfiler::GetObjectByHeapObjectIdRequest req; + req.id = msgId++; + req.objectId = std::to_string(objID); + auto resp = send< + m::heapProfiler::GetObjectByHeapObjectIdRequest, + m::heapProfiler::GetObjectByHeapObjectIdResponse>(conn, req); + EXPECT_EQ(resp.result.type, type); + EXPECT_EQ(resp.result.className, className); + EXPECT_EQ(resp.result.description, description); + if (subtype) { + EXPECT_EQ(resp.result.subtype, subtype); + } + + // Check that fetching the object by heap snapshot ID works. + m::heapProfiler::GetHeapObjectIdRequest idReq; + idReq.id = msgId++; + idReq.objectId = resp.result.objectId.value(); + auto idResp = send< + m::heapProfiler::GetHeapObjectIdRequest, + m::heapProfiler::GetHeapObjectIdResponse>(conn, idReq); + EXPECT_EQ(atoi(idResp.heapSnapshotObjectId.c_str()), objID); + }; + + // Test once before a collection. + testObject(globalObjID, "object", "Object", "Object", nullptr); + testObject(storedObjID, "object", "Array", "Array(3)", "array"); + // Force a collection to move the heap. + runtime->instrumentation().collectGarbage("test"); + // A collection should not disturb the unique ID lookup, and it should be the + // same object as before. Note that it won't have the same remote ID, because + // Hermes doesn't do uniquing. + testObject(globalObjID, "object", "Object", "Object", nullptr); + testObject(storedObjID, "object", "Array", "Array(3)", "array"); + + // Resume and exit. + send(conn, msgId++); + expectNotification(conn); +} + } // namespace chrome } // namespace inspector } // namespace hermes diff --git a/android/ReactCommon/hermes/inspector/tools/message_types.txt b/android/ReactCommon/hermes/inspector/tools/message_types.txt index 5f009532a9f9d..6267e82ee0ef0 100644 --- a/android/ReactCommon/hermes/inspector/tools/message_types.txt +++ b/android/ReactCommon/hermes/inspector/tools/message_types.txt @@ -22,8 +22,12 @@ HeapProfiler.reportHeapSnapshotProgress HeapProfiler.takeHeapSnapshot HeapProfiler.startTrackingHeapObjects HeapProfiler.stopTrackingHeapObjects +HeapProfiler.startSampling +HeapProfiler.stopSampling HeapProfiler.heapStatsUpdate HeapProfiler.lastSeenObjectId +HeapProfiler.getObjectByHeapObjectId +HeapProfiler.getHeapObjectId Runtime.consoleAPICalled Runtime.evaluate Runtime.executionContextCreated diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/CommandTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/CommandTest.js index 2b2690ed8745f..5be283814000a 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/CommandTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/CommandTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { Command } from '../src/Command.js'; +import {Command} from '../src/Command.js'; test('parses simple command', () => { let obj = { diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/EventTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/EventTest.js index da45f69963671..00cdcb7e42fe8 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/EventTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/EventTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { Event } from '../src/Event.js'; +import {Event} from '../src/Event.js'; test('parses simple event', () => { let obj = { diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/GraphTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/GraphTest.js index 5e0958ec85671..5f443e47903ce 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/GraphTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/GraphTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { Graph } from '../src/Graph.js'; +import {Graph} from '../src/Graph.js'; // graph looks like this before test: https://pxl.cl/9k8t let graph = null; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/HeaderWriterTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/HeaderWriterTest.js index c6977e358cb59..44bd13aac1a89 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/HeaderWriterTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/HeaderWriterTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { expectCodeIsEqual, FakeWritable } from '../src/TestHelpers'; +import {expectCodeIsEqual, FakeWritable} from '../src/TestHelpers'; import { emitNotificationDecl, emitRequestDecl, diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/ImplementationWriterTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/ImplementationWriterTest.js index 9f9decb9f1530..4437bb144dccf 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/ImplementationWriterTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/ImplementationWriterTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { expectCodeIsEqual, FakeWritable } from '../src/TestHelpers'; +import {expectCodeIsEqual, FakeWritable} from '../src/TestHelpers'; import { emitNotificationDef, emitRequestDef, diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/PropertyTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/PropertyTest.js index 6131a14aaaf66..583fcdf737b8a 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/PropertyTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/PropertyTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { Property } from '../src/Property.js'; +import {Property} from '../src/Property.js'; test('parses required primitive prop', () => { let obj = { diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/TypeTest.js b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/TypeTest.js index 509745fac4a5b..6f5a8649ba146 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/TypeTest.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/__tests__/TypeTest.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -import { Type } from '../src/Type.js'; +import {Type} from '../src/Type.js'; test('parses primitive type', () => { let obj = { diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/package.json b/android/ReactCommon/hermes/inspector/tools/msggen/package.json index bcd465c61e42e..6e42007bf68bf 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/package.json +++ b/android/ReactCommon/hermes/inspector/tools/msggen/package.json @@ -11,6 +11,10 @@ "watch": "babel src --out-dir bin --source-maps --watch", "test": "jest" }, + "dependencies": { + "devtools-protocol": "0.0.730699", + "yargs": "^14.2.0" + }, "devDependencies": { "@babel/cli": "^7.2.0", "@babel/core": "^7.2.0", @@ -20,19 +24,18 @@ "@babel/preset-flow": "^7.2.0", "diff": "3.5.0", "extend": "3.0.2", - "jest": "^24.9.0", + "jest": "^26.6.3", "nwmatcher": "1.4.4", "randomatic": "3.0.0", "sshpk": "1.16.1", "webpack": "^4.41.0" }, + "resolutions": { + "set-value": "^4.1.0" + }, "jest": { "transform": { ".*": "/node_modules/babel-jest" } - }, - "dependencies": { - "devtools-protocol": "0.0.730699", - "yargs": "^14.2.0" } } diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/Command.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/Command.js index 2d1678f8ef52b..08b74abcb73a1 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/Command.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/Command.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import {Property} from './Property'; import {toCppNamespace, toCppType} from './Converters'; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/Converters.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/Converters.js index 277c9fd1631f2..2eb43df111b0a 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/Converters.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/Converters.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - export function toCppNamespace(domain: string): string { return domain.substr(0, 1).toLowerCase() + domain.substr(1); } diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/Event.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/Event.js index 2f2849ba6790c..7e04311a68672 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/Event.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/Event.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import {Property} from './Property'; import {toCppNamespace, toCppType} from './Converters'; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/Graph.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/Graph.js index 3ceb3572bc3bf..98fc5e926dad7 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/Graph.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/Graph.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import invariant from 'assert'; type NodeId = string; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/HeaderWriter.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/HeaderWriter.js index d02a6ab6e28f1..d217c2e86b4f0 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/HeaderWriter.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/HeaderWriter.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import {Writable} from 'stream'; import {GeneratedHeader} from './GeneratedHeader'; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/ImplementationWriter.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/ImplementationWriter.js index dc4c93a17ed40..7dea2bb04d3b4 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/ImplementationWriter.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/ImplementationWriter.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import {Writable} from 'stream'; import {GeneratedHeader} from './GeneratedHeader'; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/Property.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/Property.js index ab93dd05baeae..bfcf5d1a23760 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/Property.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/Property.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import { jsTypeToCppType, toCppNamespace, @@ -156,6 +154,7 @@ class RefProperty extends Property { constructor(domain: string, obj: any) { super(domain, obj); this.$ref = obj.$ref; + this.recursive = obj.recursive; } getRefDebuggerName(): ?string { diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/TestHelpers.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/TestHelpers.js index 2600167e11af4..476dc195734c8 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/TestHelpers.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/TestHelpers.js @@ -5,9 +5,7 @@ * LICENSE file in the root directory of this source tree. */ - /*global expect*/ - -'use strict'; +/*global expect*/ // munges string so that it's nice to look at in a test diff function strip(str) { diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/Type.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/Type.js index 7866ecd09215a..d91432f45693f 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/Type.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/Type.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import {Property} from './Property'; import {jsTypeToCppType, toCppNamespace, toCppType} from './Converters'; diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/src/index.js b/android/ReactCommon/hermes/inspector/tools/msggen/src/index.js index adcbdea071eda..b1452d509f91e 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/src/index.js +++ b/android/ReactCommon/hermes/inspector/tools/msggen/src/index.js @@ -8,8 +8,6 @@ * @format */ -'use strict'; - import fs from 'fs'; import yargs from 'yargs'; @@ -23,7 +21,7 @@ import {PropsType, Type} from './Type'; import {HeaderWriter} from './HeaderWriter'; import {ImplementationWriter} from './ImplementationWriter'; -// $FlowFixMe: this isn't a module, just a JSON file. +// $FlowFixMe[cannot-resolve-module] : this isn't a module, just a JSON file. const standard = require('devtools-protocol/json/js_protocol.json'); const custom = require('../src/custom.json'); diff --git a/android/ReactCommon/hermes/inspector/tools/msggen/yarn.lock b/android/ReactCommon/hermes/inspector/tools/msggen/yarn.lock index 47b1dacda9373..b6b1be6b96f53 100644 --- a/android/ReactCommon/hermes/inspector/tools/msggen/yarn.lock +++ b/android/ReactCommon/hermes/inspector/tools/msggen/yarn.lock @@ -25,6 +25,18 @@ dependencies: "@babel/highlight" "^7.0.0" +"@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/compat-data@^7.13.15": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.15.tgz#7e8eea42d0b64fda2b375b22d06c605222e848f4" + integrity sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA== + "@babel/core@^7.1.0", "@babel/core@^7.2.0": version "7.7.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.7.tgz#ee155d2e12300bcc0cff6a8ad46f2af5063803e9" @@ -45,7 +57,37 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.7.4", "@babel/generator@^7.7.7": +"@babel/core@^7.7.5": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.16.tgz#7756ab24396cc9675f1c3fcd5b79fcce192ea96a" + integrity sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.16" + "@babel/helper-compilation-targets" "^7.13.16" + "@babel/helper-module-transforms" "^7.13.14" + "@babel/helpers" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.15" + "@babel/types" "^7.13.16" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.13.16": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.16.tgz#0befc287031a201d84cdfc173b46b320ae472d14" + integrity sha512-grBBR75UnKOcUWMp8WoDxNsWCFl//XCK6HWTrBQKTr5SV9f5g0pNOjdyzi/DTBv12S9GnYPInIXQBTky7OXEMg== + dependencies: + "@babel/types" "^7.13.16" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/generator@^7.7.4", "@babel/generator@^7.7.7": version "7.7.7" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.7.tgz#859ac733c44c74148e1a72980a64ec84b85f4f45" integrity sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ== @@ -79,6 +121,16 @@ "@babel/traverse" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helper-compilation-targets@^7.13.16": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" + integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== + dependencies: + "@babel/compat-data" "^7.13.15" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.14.5" + semver "^6.3.0" + "@babel/helper-create-regexp-features-plugin@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz#6d5762359fd34f4da1500e4cff9955b5299aaf59" @@ -104,6 +156,15 @@ "@babel/traverse" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" + "@babel/helper-function-name@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" @@ -113,6 +174,13 @@ "@babel/template" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-get-function-arity@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" @@ -127,6 +195,13 @@ dependencies: "@babel/types" "^7.7.4" +"@babel/helper-member-expression-to-functions@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" + integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-member-expression-to-functions@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz#356438e2569df7321a8326644d4b790d2122cb74" @@ -134,6 +209,13 @@ dependencies: "@babel/types" "^7.7.4" +"@babel/helper-module-imports@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-module-imports@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" @@ -141,6 +223,20 @@ dependencies: "@babel/types" "^7.7.4" +"@babel/helper-module-transforms@^7.13.14": + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef" + integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== + dependencies: + "@babel/helper-module-imports" "^7.13.12" + "@babel/helper-replace-supers" "^7.13.12" + "@babel/helper-simple-access" "^7.13.12" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.13" + "@babel/types" "^7.13.14" + "@babel/helper-module-transforms@^7.7.4", "@babel/helper-module-transforms@^7.7.5": version "7.7.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz#d044da7ffd91ec967db25cd6748f704b6b244835" @@ -153,6 +249,13 @@ "@babel/types" "^7.7.4" lodash "^4.17.13" +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-optimise-call-expression@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz#034af31370d2995242aa4df402c3b7794b2dcdf2" @@ -165,6 +268,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== + "@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" @@ -183,6 +291,16 @@ "@babel/traverse" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helper-replace-supers@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" + integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.13.12" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.12" + "@babel/helper-replace-supers@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz#3c881a6a6a7571275a72d82e6107126ec9e2cdd2" @@ -193,6 +311,13 @@ "@babel/traverse" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helper-simple-access@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" + integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-simple-access@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294" @@ -201,6 +326,13 @@ "@babel/template" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-split-export-declaration@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8" @@ -208,6 +340,16 @@ dependencies: "@babel/types" "^7.7.4" +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/helper-validator-option@^7.12.17": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== + "@babel/helper-wrap-function@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace" @@ -218,6 +360,15 @@ "@babel/traverse" "^7.7.4" "@babel/types" "^7.7.4" +"@babel/helpers@^7.13.16": + version "7.13.17" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.17.tgz#b497c7a00e9719d5b613b8982bda6ed3ee94caf6" + integrity sha512-Eal4Gce4kGijo1/TGJdqp3WuhllaMLSrW6XcL0ulyUAQOuxHcCafZE8KHg9857gcTehsm/v7RcOx2+jp0Ryjsg== + dependencies: + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.17" + "@babel/types" "^7.13.17" + "@babel/helpers@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" @@ -236,11 +387,25 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.4", "@babel/parser@^7.7.7": +"@babel/highlight@^7.12.13": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" + integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.7.4", "@babel/parser@^7.7.7": version "7.7.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.7.tgz#1b886595419cf92d811316d5b715a53ff38b4937" integrity sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw== +"@babel/parser@^7.12.13", "@babel/parser@^7.13.16": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.16.tgz#0f18179b0448e6939b1f3f5c4c355a3a9bcdfd37" + integrity sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw== + "@babel/plugin-proposal-async-generator-functions@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz#0351c5ac0a9e927845fffd5b82af476947b7ce6d" @@ -297,6 +462,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-dynamic-import@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz#29ca3b4415abfe4a5ec381e903862ad1a54c3aec" @@ -311,6 +497,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz#86e63f7d2e22f9e27129ac4e83ea989a382e86cc" @@ -318,13 +511,48 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.7.4": +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46" integrity sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz#a3e38f59f4b6233867b4a92dcb0ee05b2c334aa6" @@ -332,6 +560,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + "@babel/plugin-syntax-top-level-await@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz#bd7d8fa7b9fee793a36e4027fd6dd1aa32f946da" @@ -339,6 +581,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" + integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-transform-arrow-functions@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz#76309bd578addd8aee3b379d809c802305a98a12" @@ -656,7 +905,16 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-flow-strip-types" "^7.7.4" -"@babel/template@^7.4.0", "@babel/template@^7.7.4": +"@babel/template@^7.12.13", "@babel/template@^7.3.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/template@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" integrity sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw== @@ -665,7 +923,7 @@ "@babel/parser" "^7.7.4" "@babel/types" "^7.7.4" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" integrity sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw== @@ -680,7 +938,21 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.7.4": +"@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.13.15", "@babel/traverse@^7.13.17": + version "7.13.17" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.17.tgz#c85415e0c7d50ac053d758baec98b28b2ecfeea3" + integrity sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.16" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.16" + "@babel/types" "^7.13.17" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" integrity sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA== @@ -689,6 +961,19 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.13.16", "@babel/types@^7.13.17", "@babel/types@^7.3.3": + version "7.13.17" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.17.tgz#48010a115c9fba7588b4437dd68c9469012b38b4" + integrity sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -697,158 +982,211 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@jest/console@^24.7.1", "@jest/console@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" - integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: - "@jest/source-map" "^24.9.0" - chalk "^2.0.1" - slash "^2.0.0" + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" -"@jest/core@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" - integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== - dependencies: - "@jest/console" "^24.7.1" - "@jest/reporters" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.1.15" - jest-changed-files "^24.9.0" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-resolve-dependencies "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - jest-watcher "^24.9.0" - micromatch "^3.1.10" - p-each-series "^1.0.0" - realpath-native "^1.1.0" - rimraf "^2.5.4" - slash "^2.0.0" - strip-ansi "^5.0.0" - -"@jest/environment@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" - integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== - dependencies: - "@jest/fake-timers" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - -"@jest/fake-timers@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" - integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== - dependencies: - "@jest/types" "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - -"@jest/reporters@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" - integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + graceful-fs "^4.2.4" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + dependencies: + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== + dependencies: + "@jest/types" "^26.6.2" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" + +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - istanbul-lib-coverage "^2.0.2" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.4" - istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.2.6" - jest-haste-map "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - node-notifier "^5.4.2" - slash "^2.0.0" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + slash "^3.0.0" source-map "^0.6.0" - string-length "^2.0.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + optionalDependencies: + node-notifier "^8.0.0" -"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" - integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== dependencies: callsites "^3.0.0" - graceful-fs "^4.1.15" + graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" - integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== +"@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== dependencies: - "@jest/console" "^24.9.0" - "@jest/types" "^24.9.0" + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" - integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== dependencies: - "@jest/test-result" "^24.9.0" - jest-haste-map "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" + "@jest/test-result" "^26.6.2" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" -"@jest/transform@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" - integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^24.9.0" - babel-plugin-istanbul "^5.1.0" - chalk "^2.0.1" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.1.15" - jest-haste-map "^24.9.0" - jest-regex-util "^24.9.0" - jest-util "^24.9.0" - micromatch "^3.1.10" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" pirates "^4.0.1" - realpath-native "^1.1.0" - slash "^2.0.0" + slash "^3.0.0" source-map "^0.6.1" - write-file-atomic "2.4.1" + write-file-atomic "^3.0.0" -"@jest/types@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" - integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^13.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" -"@types/babel__core@^7.1.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" - integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + version "7.1.14" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" + integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -878,11 +1216,30 @@ dependencies: "@babel/types" "^7.3.0" +"@types/babel__traverse@^7.0.4": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" + integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/graceful-fs@^4.1.2": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + dependencies: + "@types/node" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== +"@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + "@types/istanbul-lib-report@*": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" @@ -890,175 +1247,188 @@ dependencies: "@types/istanbul-lib-coverage" "*" -"@types/istanbul-reports@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" - integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== dependencies: - "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/node@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.0.tgz#557dd0da4a6dca1407481df3bbacae0cd6f68042" + integrity sha512-YN1d+ae2MCb4U0mMa+Zlb5lWTdpFShbAj5nmte6lel27waMMBfivrm0prC16p/Di3DyTrmerrYUT8/145HXxVw== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/prettier@^2.0.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" + integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA== + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== "@types/yargs-parser@*": version "13.1.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== -"@types/yargs@^13.0.0": - version "13.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.3.tgz#76482af3981d4412d65371a318f992d33464a380" - integrity sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ== +"@types/yargs@^15.0.0": + version "15.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" + integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" - integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== - dependencies: - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - -"@webassemblyjs/floating-point-hex-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" - integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== - -"@webassemblyjs/helper-api-error@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" - integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== - -"@webassemblyjs/helper-buffer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" - integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== - -"@webassemblyjs/helper-code-frame@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" - integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== - dependencies: - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/helper-fsm@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" - integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== - -"@webassemblyjs/helper-module-context@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" - integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== - dependencies: - "@webassemblyjs/ast" "1.8.5" - mamacro "^0.0.3" - -"@webassemblyjs/helper-wasm-bytecode@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" - integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== - -"@webassemblyjs/helper-wasm-section@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" - integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - -"@webassemblyjs/ieee754@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" - integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" - integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" - integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== - -"@webassemblyjs/wasm-edit@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" - integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/helper-wasm-section" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-opt" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/wasm-gen@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" - integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wasm-opt@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" - integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - -"@webassemblyjs/wasm-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" - integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wast-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" - integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/floating-point-hex-parser" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-code-frame" "1.8.5" - "@webassemblyjs/helper-fsm" "1.8.5" +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" - integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -1071,33 +1441,38 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" - integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== +abab@^2.0.3, abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -acorn-globals@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" + acorn "^7.1.1" + acorn-walk "^7.1.1" -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^5.5.3: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^6.0.1, acorn@^6.2.1: - version "6.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" - integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== +acorn@^8.1.0: + version "8.2.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.1.tgz#0d36af126fb6755095879c1dc6fd7edf7d60a5fb" + integrity sha512-z716cpm5TX4uzOzILx8PavOE6C6DKshHDw1aQN52M/yNSqE9s5O8SMfyhCCfCJ3HmTL0NkVOi+8a/55T7YB3bg== ajv-errors@^1.0.0: version "1.0.1" @@ -1105,35 +1480,37 @@ ajv-errors@^1.0.0: integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" - integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: - fast-deep-equal "^2.0.1" + fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" -ansi-regex@^4.0.0, ansi-regex@^4.1.0: +ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1141,6 +1518,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1149,11 +1533,26 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@^3.0.3, anymatch@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -1169,24 +1568,20 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" - integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== dependencies: bn.js "^4.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" asn1@~0.2.3: version "0.2.4" @@ -1213,27 +1608,17 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -atob@^2.1.1: +atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== @@ -1248,18 +1633,19 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" integrity sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A== -babel-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== - dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.0" - chalk "^2.4.2" - slash "^2.0.0" +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" babel-plugin-dynamic-import-node@^2.3.0: version "2.3.0" @@ -1268,40 +1654,62 @@ babel-plugin-dynamic-import-node@^2.3.0: dependencies: object.assign "^4.1.0" -babel-plugin-istanbul@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" - integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - find-up "^3.0.0" - istanbul-lib-instrument "^3.3.0" - test-exclude "^5.2.3" - -babel-plugin-jest-hoist@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" - integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== - dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-preset-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" - integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== - dependencies: - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.9.0" +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.0.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" @@ -1333,6 +1741,11 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -1345,10 +1758,15 @@ bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== brace-expansion@^1.1.7: version "1.1.11" @@ -1374,22 +1792,22 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -brorand@^1.0.1: +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-process-hrtime@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" - integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== - -browser-resolve@^1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" @@ -1422,26 +1840,28 @@ browserify-des@^1.0.0: inherits "^2.0.1" safe-buffer "^5.1.2" -browserify-rsa@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" - integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== dependencies: - bn.js "^4.1.0" + bn.js "^5.0.0" randombytes "^2.0.1" browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" - integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= - dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" browserify-zlib@^0.2.0: version "0.2.0" @@ -1450,6 +1870,17 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@^4.14.5: + version "4.16.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.5.tgz#952825440bca8913c62d0021334cbe928ef062ae" + integrity sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A== + dependencies: + caniuse-lite "^1.0.30001214" + colorette "^1.2.2" + electron-to-chromium "^1.3.719" + escalade "^3.1.1" + node-releases "^1.1.71" + browserslist@^4.6.0, browserslist@^4.8.2: version "4.8.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289" @@ -1491,9 +1922,9 @@ builtin-status-codes@^3.0.0: integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= cacache@^12.0.2: - version "12.0.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" - integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== dependencies: bluebird "^3.5.5" chownr "^1.1.1" @@ -1536,11 +1967,21 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + caniuse-lite@^1.0.30001015: version "1.0.30001016" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz#16ea48d7d6e8caf3cad3295c2d746fe38c4e7f66" integrity sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA== +caniuse-lite@^1.0.30001214: + version "1.0.30001216" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001216.tgz#47418a082a4f952d14d8964ae739e25efb2060a9" + integrity sha512-1uU+ww/n5WCJRwUcc9UH/W6925Se5aNnem/G5QaSDga2HzvjYMs8vRbekGUN/PnTZ7ezTHcxxTEb9fgiMYwH6Q== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -1553,7 +1994,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1562,7 +2003,20 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chokidar@^2.0.2, chokidar@^2.1.8: +chalk@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -1581,17 +2035,30 @@ chokidar@^2.0.2, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.4.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chownr@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" - integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== ci-info@^2.0.0: version "2.0.0" @@ -1606,6 +2073,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -1625,11 +2097,25 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1645,11 +2131,28 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -1657,7 +2160,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.20.0, commander@~2.20.3: +commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -1702,7 +2205,7 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -1740,14 +2243,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" - integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== dependencies: bn.js "^4.1.0" - elliptic "^6.0.0" + elliptic "^6.5.3" -create-hash@^1.1.0, create-hash@^1.1.2: +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== @@ -1758,7 +2261,7 @@ create-hash@^1.1.0, create-hash@^1.1.2: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -1781,6 +2284,15 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -1798,17 +2310,22 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: - cssom "0.3.x" + cssom "~0.3.6" cyclist@^1.0.1: version "1.0.1" @@ -1822,14 +2339,14 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -1850,6 +2367,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -1860,7 +2382,12 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -define-properties@^1.1.2, define-properties@^1.1.3: +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +define-properties@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -1902,20 +2429,20 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== devtools-protocol@0.0.730699: version "0.0.730699" resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.730699.tgz#4d18f6a9b7fb7cf3f1ffe73bfe14aad66cf3b2ef" integrity sha512-dprBpuPzVIIXXL6GevzhvWe2wg836h3d5hY+n6IzzHbKLsUh6QlVmcIy15za0J3MhDFbmEH60s6uYsrw/tgBbw== -diff-sequences@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== diff@3.5.0: version "3.5.0" @@ -1936,12 +2463,12 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: - webidl-conversions "^4.0.2" + webidl-conversions "^5.0.0" duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" @@ -1966,28 +2493,43 @@ electron-to-chromium@^1.3.322: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8" integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA== -elliptic@^6.0.0: - version "6.5.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" - integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== +electron-to-chromium@^1.3.719: + version "1.3.720" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.720.tgz#f5d66df8754d993006b7b2ded15ff7738c58bd94" + integrity sha512-B6zLTxxaOFP4WZm6DrvgRk8kLFYWNhQ5TrHMC0l5WtkMXhU5UbnvWoTfeEwqOruUSlNMhVLfYak7REX6oC5Yfw== + +elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" + bn.js "^4.11.9" + brorand "^1.1.0" hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" @@ -1996,19 +2538,19 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" - integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== +enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" tapable "^1.0.0" errno@^0.1.3, errno@~0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== dependencies: prr "~1.0.1" @@ -2019,44 +2561,28 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1: - version "1.17.0-next.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0-next.1.tgz#94acc93e20b05a6e96dacb5ab2f1cb3a81fc2172" - integrity sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.0" - string.prototype.trimright "^2.1.0" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.9.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" - integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" + esprima "^4.0.1" + estraverse "^5.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: @@ -2070,32 +2596,37 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -esprima@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== events@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" - integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -2123,6 +2654,21 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2141,17 +2687,17 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" - integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== dependencies: - "@jest/types" "^24.9.0" - ansi-styles "^3.2.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.9.0" + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" extend-shallow@^2.0.1: version "2.0.1" @@ -2197,10 +2743,10 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-json-stable-stringify@^2.0.0: version "2.1.0" @@ -2220,9 +2766,9 @@ fb-watchman@^2.0.0: bser "2.1.1" figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== file-uri-to-path@1.0.0: version "1.0.0" @@ -2239,6 +2785,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -2255,6 +2808,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -2318,23 +2879,38 @@ fs.realpath@^1.0.0: integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.2.7: - version "1.2.11" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3" - integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw== + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== dependencies: bindings "^1.5.0" nan "^2.12.1" +fsevents@^2.1.2, fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -2342,6 +2918,13 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2362,6 +2945,13 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -2379,38 +2969,27 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@^4.1.2: - version "4.5.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" - integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== - dependencies: - neo-async "^2.6.0" - optimist "^0.6.1" - source-map "^0.6.1" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: - ajv "^6.5.5" + ajv "^6.12.3" har-schema "^2.0.0" has-flag@^3.0.0: @@ -2418,7 +2997,12 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -has-symbols@^1.0.0, has-symbols@^1.0.1: +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== @@ -2462,12 +3046,13 @@ has@^1.0.3: function-bind "^1.1.1" hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" @@ -2477,7 +3062,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hmac-drbg@^1.0.0: +hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= @@ -2491,12 +3076,17 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: - whatwg-encoding "^1.0.1" + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-signature@~1.2.0: version "1.2.0" @@ -2512,6 +3102,11 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2520,22 +3115,22 @@ iconv-lite@0.4.24: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" @@ -2555,7 +3150,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2570,7 +3165,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -2603,16 +3198,18 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== - is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -2620,6 +3217,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-core-module@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.3.0.tgz#d341652e3408bca69c4671b79a0954a3d349f887" + integrity sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -2634,11 +3238,6 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== - is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -2657,6 +3256,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -2679,6 +3283,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -2691,7 +3300,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -2710,33 +3319,39 @@ is-number@^4.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-regex@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== - dependencies: - has "^1.0.3" +is-potential-custom-element-name@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-primitive@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-3.0.1.tgz#98c4db1abff185485a657fc2905052b940524d05" + integrity sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w== is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-typedarray@~1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -2751,6 +3366,13 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2778,404 +3400,419 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== - dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" -istanbul-lib-report@^2.0.4: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -istanbul-lib-source-maps@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" - integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== - dependencies: - handlebars "^4.1.2" - -jest-changed-files@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" - integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== - dependencies: - "@jest/types" "^24.9.0" - execa "^1.0.0" - throat "^4.0.0" - -jest-cli@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" - integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== - dependencies: - "@jest/core" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== + dependencies: + "@jest/types" "^26.6.2" + execa "^4.0.0" + throat "^5.0.0" + +jest-cli@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== + dependencies: + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" exit "^0.1.2" - import-local "^2.0.0" + graceful-fs "^4.2.4" + import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^13.3.0" + yargs "^15.4.1" -jest-config@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" - integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.9.0" - "@jest/types" "^24.9.0" - babel-jest "^24.9.0" - chalk "^2.0.1" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" + chalk "^4.0.0" + deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^24.9.0" - jest-environment-node "^24.9.0" - jest-get-type "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - micromatch "^3.1.10" - pretty-format "^24.9.0" - realpath-native "^1.1.0" - -jest-diff@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== - dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-docblock@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" - integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== - dependencies: - detect-newline "^2.1.0" - -jest-each@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" - integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== - dependencies: - "@jest/types" "^24.9.0" - chalk "^2.0.1" - jest-get-type "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - -jest-environment-jsdom@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" - integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jsdom "^11.5.1" - -jest-environment-node@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" - integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - -jest-get-type@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" - integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== - -jest-haste-map@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" - integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== - dependencies: - "@jest/types" "^24.9.0" - anymatch "^2.0.0" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + micromatch "^4.0.2" + pretty-format "^26.6.2" + +jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" + +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" + +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.1.15" - invariant "^2.2.4" - jest-serializer "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.9.0" - micromatch "^3.1.10" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" optionalDependencies: - fsevents "^1.2.7" + fsevents "^2.1.2" -jest-jasmine2@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" - integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" co "^4.6.0" - expect "^24.9.0" + expect "^26.6.2" is-generator-fn "^2.0.0" - jest-each "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - throat "^4.0.0" - -jest-leak-detector@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" - integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== - dependencies: - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-matcher-utils@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== - dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-message-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" - integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" + throat "^5.0.0" + +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/stack-utils" "^1.0.1" - chalk "^2.0.1" - micromatch "^3.1.10" - slash "^2.0.0" - stack-utils "^1.0.1" - -jest-mock@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" - integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== - dependencies: - "@jest/types" "^24.9.0" - -jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== - -jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" - integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== - -jest-resolve-dependencies@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" - integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== - dependencies: - "@jest/types" "^24.9.0" - jest-regex-util "^24.3.0" - jest-snapshot "^24.9.0" - -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== - dependencies: - "@jest/types" "^24.9.0" - browser-resolve "^1.11.3" - chalk "^2.0.1" - jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" - -jest-runner@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" - integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.4.2" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== + dependencies: + "@jest/types" "^26.6.2" + jest-regex-util "^26.0.0" + jest-snapshot "^26.6.2" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" exit "^0.1.2" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-docblock "^24.3.0" - jest-haste-map "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-leak-detector "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-docblock "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" source-map-support "^0.5.6" - throat "^4.0.0" - -jest-runtime@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" - integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - chalk "^2.0.1" + throat "^5.0.0" + +jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + cjs-module-lexer "^0.6.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - realpath-native "^1.1.0" - slash "^2.0.0" - strip-bom "^3.0.0" - yargs "^13.3.0" - -jest-serializer@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" - integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== - -jest-snapshot@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.4.1" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" - -jest-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" - integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== - dependencies: - "@jest/console" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/source-map" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - callsites "^3.0.0" - chalk "^2.0.1" - graceful-fs "^4.1.15" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" is-ci "^2.0.0" - mkdirp "^0.5.1" - slash "^2.0.0" - source-map "^0.6.0" + micromatch "^4.0.2" -jest-validate@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" - integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== dependencies: - "@jest/types" "^24.9.0" - camelcase "^5.3.1" - chalk "^2.0.1" - jest-get-type "^24.9.0" + "@jest/types" "^26.6.2" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" leven "^3.1.0" - pretty-format "^24.9.0" - -jest-watcher@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" - integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== - dependencies: - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - jest-util "^24.9.0" - string-length "^2.0.0" - -jest-worker@^24.6.0, jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== - dependencies: + pretty-format "^26.6.2" + +jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== + dependencies: + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.6.2" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" merge-stream "^2.0.0" - supports-color "^6.1.0" + supports-color "^7.0.0" -jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" - integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== +jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" + integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== dependencies: - import-local "^2.0.0" - jest-cli "^24.9.0" + "@jest/core" "^26.6.3" + import-local "^3.0.2" + jest-cli "^26.6.3" js-levenshtein@^1.1.3: version "1.1.6" @@ -3187,41 +3824,49 @@ js-levenshtein@^1.1.3: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^11.5.1: - version "11.12.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" - integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== - dependencies: - abab "^2.0.0" - acorn "^5.5.3" - acorn-globals "^4.1.0" - array-equal "^1.0.0" - cssom ">= 0.3.2 < 0.4.0" - cssstyle "^1.0.0" - data-urls "^1.0.0" - domexception "^1.0.1" - escodegen "^1.9.1" - html-encoding-sniffer "^1.0.2" - left-pad "^1.3.0" - nwsapi "^2.0.7" - parse5 "4.0.0" - pn "^1.1.0" - request "^2.87.0" - request-promise-native "^1.0.5" - sax "^1.2.4" - symbol-tree "^3.2.2" - tough-cookie "^2.3.4" - w3c-hr-time "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.3" - whatwg-mimetype "^2.1.0" - whatwg-url "^6.4.1" - ws "^5.2.0" +jsdom@^16.4.0: + version "16.5.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.3.tgz#13a755b3950eb938b4482c407238ddf16f0d2136" + integrity sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA== + dependencies: + abab "^2.0.5" + acorn "^8.1.0" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + html-encoding-sniffer "^2.0.1" + is-potential-custom-element-name "^1.0.0" + nwsapi "^2.2.0" + parse5 "6.0.1" + request "^2.88.2" + request-promise-native "^1.0.9" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.4" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -3234,11 +3879,16 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -3268,6 +3918,13 @@ json5@^2.1.0: dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -3298,20 +3955,15 @@ kind-of@^5.0.0: integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -left-pad@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" - integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -3325,15 +3977,10 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= loader-runner@^2.4.0: version "2.4.0" @@ -3341,12 +3988,12 @@ loader-runner@^2.4.0: integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== loader-utils@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== dependencies: big.js "^5.2.2" - emojis-list "^2.0.0" + emojis-list "^3.0.0" json5 "^1.0.1" locate-path@^3.0.0: @@ -3357,16 +4004,23 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" -lodash@^4.17.13, lodash@^4.17.15: +lodash@^4.17.13: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -3381,6 +4035,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -3389,6 +4050,13 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -3396,11 +4064,6 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -3467,6 +4130,14 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -3487,12 +4158,17 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.42.0" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: +minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= @@ -3504,20 +4180,15 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.1.1, minimist@^1.2.0: +minimist@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mississippi@^3.0.0: version "3.0.0" @@ -3543,12 +4214,12 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= +mkdirp@^0.5.1, mkdirp@^0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: - minimist "0.0.8" + minimist "^1.2.5" move-concurrently@^1.0.1: version "1.0.1" @@ -3573,9 +4244,9 @@ ms@^2.1.1: integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== nanomatch@^1.2.9: version "1.2.13" @@ -3599,10 +4270,10 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.5.0, neo-async@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nice-try@^1.0.4: version "1.0.5" @@ -3648,16 +4319,17 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^5.4.2: - version "5.4.3" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" - integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== +node-notifier@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== dependencies: growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" + is-wsl "^2.2.0" + semver "^7.3.2" shellwords "^0.1.1" - which "^1.3.0" + uuid "^8.3.0" + which "^2.0.2" node-releases@^1.1.42: version "1.1.43" @@ -3666,7 +4338,12 @@ node-releases@^1.1.42: dependencies: semver "^6.3.0" -normalize-package-data@^2.3.2: +node-releases@^1.1.71: + version "1.1.71" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" + integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== + +normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -3683,7 +4360,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -3695,12 +4372,19 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + nwmatcher@1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" integrity sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ== -nwsapi@^2.0.7: +nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== @@ -3724,12 +4408,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" - integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.0.11, object-keys@^1.0.12: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -3751,14 +4430,6 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.getownpropertydescriptors@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -3773,13 +4444,12 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" + mimic-fn "^2.1.0" optionator@^0.8.1: version "0.8.3" @@ -3798,22 +4468,20 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= - dependencies: - p-reduce "^1.0.0" +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-limit@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" @@ -3824,10 +4492,12 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" p-try@^2.0.0: version "2.2.0" @@ -3835,9 +4505,9 @@ p-try@^2.0.0: integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pako@~1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== parallel-transform@^1.1.0: version "1.2.0" @@ -3848,30 +4518,31 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" -parse-asn1@^5.0.0: - version "5.1.5" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" - integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== dependencies: - asn1.js "^4.0.0" + asn1.js "^5.2.0" browserify-aes "^1.0.0" - create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: + "@babel/code-frame" "^7.0.0" error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== pascalcase@^0.1.1: version "0.1.1" @@ -3893,6 +4564,11 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -3903,22 +4579,20 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" - integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -3931,10 +4605,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" + integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== pify@^4.0.1: version "4.0.1" @@ -3955,10 +4629,12 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" posix-character-classes@^0.1.0: version "0.1.1" @@ -3970,15 +4646,15 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -pretty-format@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== +pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== dependencies: - "@jest/types" "^24.9.0" - ansi-regex "^4.0.0" - ansi-styles "^3.2.0" - react-is "^16.8.4" + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" private@^0.1.6: version "0.1.8" @@ -4013,11 +4689,16 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -psl@^1.1.24, psl@^1.1.28: +psl@^1.1.28: version "1.6.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.6.0.tgz#60557582ee23b6c43719d9890fb4170ecd91e110" integrity sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA== +psl@^1.1.33: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" @@ -4060,7 +4741,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -4094,7 +4775,7 @@ randomatic@3.0.0: kind-of "^6.0.0" math-random "^1.0.1" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -4109,32 +4790,34 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -react-is@^16.8.4: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" - integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -4144,6 +4827,15 @@ read-pkg@^3.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -4153,12 +4845,12 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: - util.promisify "^1.0.0" + picomatch "^2.2.1" regenerate-unicode-properties@^8.1.0: version "8.1.0" @@ -4217,35 +4909,35 @@ remove-trailing-separator@^1.0.1: integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: - lodash "^4.17.15" + lodash "^4.17.19" -request-promise-native@^1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== +request-promise-native@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: - request-promise-core "1.1.3" + request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== +request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -4254,7 +4946,7 @@ request@^2.87.0: extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.0" + har-validator "~5.1.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -4264,7 +4956,7 @@ request@^2.87.0: performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.4.3" + tough-cookie "~2.5.0" tunnel-agent "^0.6.0" uuid "^3.3.2" @@ -4278,28 +4970,23 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: - resolve-from "^3.0.0" + resolve-from "^5.0.0" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - resolve@^1.10.0, resolve@^1.3.2: version "1.14.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.1.tgz#9e018c540fcf0c427d678b9931cbf45e984bcaff" @@ -4307,6 +4994,14 @@ resolve@^1.10.0, resolve@^1.3.2: dependencies: path-parse "^1.0.6" +resolve@^1.18.1: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -4319,6 +5014,13 @@ rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -4339,10 +5041,10 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -4376,10 +5078,12 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" schema-utils@^1.0.0: version "1.0.0" @@ -4400,30 +5104,37 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -serialize-javascript@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" - integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +semver@^7.3.2: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== +set-value@^2.0.0, set-value@^2.0.1, set-value@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-4.1.0.tgz#aa433662d87081b75ad88a4743bd450f044e7d09" + integrity sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw== dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" + is-plain-object "^2.0.4" + is-primitive "^3.0.1" setimmediate@^1.0.4: version "1.0.5" @@ -4445,11 +5156,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -4470,6 +5193,11 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -4506,17 +5234,17 @@ source-list-map@^2.0.0: integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: - atob "^2.1.1" + atob "^2.1.2" decode-uri-component "^0.2.0" resolve-url "^0.2.1" source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.6: version "0.5.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== @@ -4524,10 +5252,18 @@ source-map-support@^0.5.6, source-map-support@~0.5.12: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" @@ -4539,6 +5275,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + spdx-correct@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" @@ -4565,13 +5306,18 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== -split-string@^3.0.1, split-string@^3.0.2: +split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + sshpk@1.16.1, sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -4588,16 +5334,18 @@ sshpk@1.16.1, sshpk@^1.7.0: tweetnacl "~0.14.0" ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + version "6.0.2" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" + integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== dependencies: figgy-pudding "^3.5.1" -stack-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" - integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== +stack-utils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== + dependencies: + escape-string-regexp "^2.0.0" static-extend@^0.1.1: version "0.1.2" @@ -4644,13 +5392,13 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -string-length@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" - integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: - astral-regex "^1.0.0" - strip-ansi "^4.0.0" + char-regex "^1.0.2" + strip-ansi "^6.0.0" string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" @@ -4661,23 +5409,16 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.trimleft@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimright@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" -string_decoder@^1.0.0: +string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -4691,13 +5432,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -4705,16 +5439,28 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -4722,14 +5468,22 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: - has-flag "^3.0.0" + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" -symbol-tree@^3.2.2: +symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -4739,44 +5493,51 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + terser-webpack-plugin@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" - integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== dependencies: cacache "^12.0.2" find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^2.1.2" + serialize-javascript "^4.0.0" source-map "^0.6.1" terser "^4.1.2" webpack-sources "^1.4.0" worker-farm "^1.7.0" terser@^4.1.2: - version "4.4.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.3.tgz#401abc52b88869cf904412503b1eb7da093ae2f0" - integrity sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA== + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: commander "^2.20.0" source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: - glob "^7.1.3" + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" -throat@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" - integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== through2@^2.0.0: version "2.0.5" @@ -4787,9 +5548,9 @@ through2@^2.0.0: xtend "~4.0.1" timers-browserify@^2.0.4: - version "2.0.11" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" - integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== dependencies: setimmediate "^1.0.4" @@ -4823,6 +5584,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -4833,7 +5601,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@^2.3.3, tough-cookie@^2.3.4: +tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -4841,25 +5609,21 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: - psl "^1.1.24" - punycode "^1.4.1" + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= +tr46@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" + integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== dependencies: - punycode "^2.1.0" - -tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + punycode "^2.1.1" tty-browserify@0.0.0: version "0.0.0" @@ -4885,19 +5649,38 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -uglify-js@^3.1.4: - version "3.7.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.2.tgz#cb1a601e67536e9ed094a92dd1e333459643d3f9" - integrity sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA== - dependencies: - commander "~2.20.3" - source-map "~0.6.1" - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -4945,6 +5728,11 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -4959,9 +5747,9 @@ upath@^1.1.1: integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" @@ -4983,19 +5771,11 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -5015,6 +5795,20 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-to-istanbul@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz#04bfd1026ba4577de5472df4f5e89af49de5edda" + integrity sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -5037,12 +5831,19 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== dependencies: - browser-process-hrtime "^0.1.2" + xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: version "1.0.7" @@ -5051,19 +5852,33 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" -watchpack@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== dependencies: - chokidar "^2.0.2" graceful-fs "^4.1.2" neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" @@ -5074,86 +5889,79 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-map "~0.6.1" webpack@^4.41.0: - version "4.41.4" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.4.tgz#4bec4125224bdf50efa8be6226c19047599cd034" - integrity sha512-Lc+2uB6NjpCWsHI3trkoISOI64h9QYIXenbEWj3bn3oyjfB1lEBXjWAfAyY2sM0rZn41oD5V91OLwKRwS6Wp8Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.1" + version "4.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.5.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" loader-utils "^1.2.3" memory-fs "^0.4.1" micromatch "^3.1.10" - mkdirp "^0.5.1" + mkdirp "^0.5.3" neo-async "^2.6.1" node-libs-browser "^2.2.1" schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" + watchpack "^1.7.4" webpack-sources "^1.4.1" -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: +whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: +whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" + integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" + lodash "^4.7.0" + tr46 "^2.0.2" + webidl-conversions "^6.1.0" which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9, which@^1.3.0: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= - worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -5170,54 +5978,64 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" - integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: - graceful-fs "^4.1.11" imurmurhash "^0.1.4" + is-typedarray "^1.0.0" signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" -ws@^5.2.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" - integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== - dependencies: - async-limiter "~1.0.0" +ws@^7.4.4: + version "7.4.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" + integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yargs-parser@^15.0.0: version "15.0.0" @@ -5227,12 +6045,21 @@ yargs-parser@^15.0.0: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^14.2.0: + version "14.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5" + integrity sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA== dependencies: cliui "^5.0.0" + decamelize "^1.2.0" find-up "^3.0.0" get-caller-file "^2.0.1" require-directory "^2.1.1" @@ -5241,21 +6068,21 @@ yargs@^13.3.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^15.0.0" -yargs@^14.2.0: - version "14.2.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5" - integrity sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA== +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: - cliui "^5.0.0" + cliui "^6.0.0" decamelize "^1.2.0" - find-up "^3.0.0" + find-up "^4.1.0" get-caller-file "^2.0.1" require-directory "^2.1.1" require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^3.0.0" + string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^15.0.0" + yargs-parser "^18.1.2" diff --git a/android/ReactCommon/jsi/Android.mk b/android/ReactCommon/jsi/Android.mk index 40c7042c16e37..3fce2059e2d75 100644 --- a/android/ReactCommon/jsi/Android.mk +++ b/android/ReactCommon/jsi/Android.mk @@ -17,7 +17,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS := -fexceptions -frtti -O3 LOCAL_SHARED_LIBRARIES := libfolly_json glog -include $(BUILD_STATIC_LIBRARY) +include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) @@ -32,8 +32,8 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS := -fexceptions -frtti -O3 LOCAL_SHARED_LIBRARIES := libfolly_json libjsc glog -ifeq ($(BUILD_FABRIC),true) - LOCAL_CFLAGS += -DRN_FABRIC_ENABLED -endif +# TODO: Remove this flag when ready. +# Android has this enabled by default, but the flag is still needed for iOS. +LOCAL_CFLAGS += -DRN_FABRIC_ENABLED include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactCommon/jsi/BUCK b/android/ReactCommon/jsi/BUCK index ecaba03aa539c..a3bfde64b98d8 100644 --- a/android/ReactCommon/jsi/BUCK +++ b/android/ReactCommon/jsi/BUCK @@ -1,5 +1,3 @@ -# BUILD FILE SYNTAX: SKYLARK - load("//tools/build_defs/oss:rn_defs.bzl", "APPLE", "IOS", "MACOSX", "react_native_xplat_dep", "rn_xplat_cxx_library") rn_xplat_cxx_library( @@ -16,11 +14,6 @@ rn_xplat_cxx_library( ], compiler_flags = [ "-O3", - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - "-Werror", "-Wextra", "-Wcast-qual", "-Wdelete-non-virtual-dtor", @@ -47,10 +40,6 @@ rn_xplat_cxx_library( exported_headers = [ "jsi/JSIDynamic.h", ], - compiler_flags = [ - "-fexceptions", - "-frtti", - ], fbobjc_force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ @@ -79,8 +68,6 @@ rn_xplat_cxx_library( fbobjc_frameworks = [ "$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework", ], - # TODO (T55502220): Remove when iOS 9.0 deprecation is complete everywhere. - fbobjc_target_sdk_version = "9.0", labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = APPLE, visibility = ["PUBLIC"], diff --git a/android/ReactCommon/jsi/JSCRuntime.cpp b/android/ReactCommon/jsi/JSCRuntime.cpp index b6e6062847cc8..c9465b33dff58 100644 --- a/android/ReactCommon/jsi/JSCRuntime.cpp +++ b/android/ReactCommon/jsi/JSCRuntime.cpp @@ -50,6 +50,9 @@ class JSCRuntime : public jsi::Runtime { jsi::Value evaluateJavaScript( const std::shared_ptr &buffer, const std::string &sourceURL) override; + + bool drainMicrotasks(int maxMicrotasksHint = -1) override; + jsi::Object global() override; std::string description() override; @@ -432,6 +435,10 @@ jsi::Value JSCRuntime::evaluateJavaScript( return createValue(res); } +bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) { + return true; +} + jsi::Object JSCRuntime::global() { return createObject(JSContextGetGlobalObject(ctx_)); } diff --git a/android/ReactCommon/jsi/React-jsi.podspec b/android/ReactCommon/jsi/React-jsi.podspec index d250597b12721..92bc7f92d07ca 100644 --- a/android/ReactCommon/jsi/React-jsi.podspec +++ b/android/ReactCommon/jsi/React-jsi.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,17 +27,17 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.exclude_files = "**/test/*" s.framework = "JavaScriptCore" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "jsi" s.default_subspec = "Default" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/android/ReactCommon/jsi/jsi/CMakeLists.txt b/android/ReactCommon/jsi/jsi/CMakeLists.txt index 121d697fd8e76..acfa48395e518 100644 --- a/android/ReactCommon/jsi/jsi/CMakeLists.txt +++ b/android/ReactCommon/jsi/jsi/CMakeLists.txt @@ -24,7 +24,7 @@ endif() if (HERMES_ENABLE_BITCODE) list(APPEND jsi_compile_flags "-fembed-bitcode") endif () -target_compile_options(jsi PUBLIC ${jsi_compile_flags}) +target_compile_options(jsi PRIVATE ${jsi_compile_flags}) install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/jsi/" DESTINATION include FILES_MATCHING PATTERN "*.h" diff --git a/android/ReactCommon/jsi/jsi/decorator.h b/android/ReactCommon/jsi/jsi/decorator.h index 9110145e17788..be6f4f568b68e 100644 --- a/android/ReactCommon/jsi/jsi/decorator.h +++ b/android/ReactCommon/jsi/jsi/decorator.h @@ -126,6 +126,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { const std::shared_ptr& js) override { return plain().evaluatePreparedJavaScript(js); } + bool drainMicrotasks(int maxMicrotasksHint) override { + return plain().drainMicrotasks(maxMicrotasksHint); + } Object global() override { return plain().global(); } @@ -348,6 +351,14 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { plain().instrumentation().stopTrackingHeapObjectStackTraces(); } + void startHeapSampling(size_t samplingInterval) override { + plain().instrumentation().startHeapSampling(samplingInterval); + } + + void stopHeapSampling(std::ostream& os) override { + plain().instrumentation().stopHeapSampling(os); + } + void createSnapshotToFile(const std::string& path) override { plain().instrumentation().createSnapshotToFile(path); } @@ -483,6 +494,10 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; return RD::evaluatePreparedJavaScript(js); } + bool drainMicrotasks(int maxMicrotasksHint) override { + Around around{with_}; + return RD::drainMicrotasks(maxMicrotasksHint); + } Object global() override { Around around{with_}; return RD::global(); diff --git a/android/ReactCommon/jsi/jsi/instrumentation.h b/android/ReactCommon/jsi/jsi/instrumentation.h index 0a9f48abb52b1..a337a7a841510 100644 --- a/android/ReactCommon/jsi/jsi/instrumentation.h +++ b/android/ReactCommon/jsi/jsi/instrumentation.h @@ -76,6 +76,19 @@ class JSI_EXPORT Instrumentation { /// Stop capture JS stack-traces for JS heap allocated objects. virtual void stopTrackingHeapObjectStackTraces() = 0; + /// Start a heap sampling profiler that will sample heap allocations, and the + /// stack trace they were allocated at. Reports a summary of which functions + /// allocated the most. + /// \param samplingInterval The number of bytes allocated to wait between + /// samples. This will be used as the expected value of a poisson + /// distribution. + virtual void startHeapSampling(size_t samplingInterval) = 0; + + /// Turns off the heap sampling profiler previously enabled via + /// \c startHeapSampling. Writes the output of the sampling heap profiler to + /// \p os. The output is a JSON formatted string. + virtual void stopHeapSampling(std::ostream& os) = 0; + /// Captures the heap to a file /// /// \param path to save the heap capture diff --git a/android/ReactCommon/jsi/jsi/jsi.cpp b/android/ReactCommon/jsi/jsi/jsi.cpp index 1054057e1fdab..be38c9787ecd8 100644 --- a/android/ReactCommon/jsi/jsi/jsi.cpp +++ b/android/ReactCommon/jsi/jsi/jsi.cpp @@ -106,6 +106,9 @@ Instrumentation& Runtime::instrumentation() { std::vector)>) override {} void stopTrackingHeapObjectStackTraces() override {} + void startHeapSampling(size_t) override {} + void stopHeapSampling(std::ostream&) override {} + void createSnapshotToFile(const std::string&) override { throw JSINativeException( "Default instrumentation cannot create a heap snapshot"); diff --git a/android/ReactCommon/jsi/jsi/jsi.h b/android/ReactCommon/jsi/jsi/jsi.h index 25c152227e6d5..ae64498f03f9d 100644 --- a/android/ReactCommon/jsi/jsi/jsi.h +++ b/android/ReactCommon/jsi/jsi/jsi.h @@ -122,7 +122,7 @@ class JSI_EXPORT HostObject { virtual void set(Runtime&, const PropNameID& name, const Value& value); // When JS wants a list of property names for the HostObject, it will - // call this method. If it throws an exception, the call will thow a + // call this method. If it throws an exception, the call will throw a // JS \c Error object. The default implementation returns empty vector. virtual std::vector getPropertyNames(Runtime& rt); }; @@ -185,6 +185,35 @@ class JSI_EXPORT Runtime { virtual Value evaluatePreparedJavaScript( const std::shared_ptr& js) = 0; + /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue. + /// + /// \param maxMicrotasksHint a hint to tell an implementation that it should + /// make a best effort not execute more than the given number. It's default + /// to -1 for infinity (unbounded execution). + /// \return true if the queue is drained or false if there is more work to do. + /// + /// When there were exceptions thrown from the execution of microtasks, + /// implementations shall discard the exceptional jobs. An implementation may + /// \throw a \c JSError object to signal the hosts to handle. In that case, an + /// implementation may or may not suspend the draining. + /// + /// Hosts may call this function again to resume the draining if it was + /// suspended due to either exceptions or the \p maxMicrotasksHint bound. + /// E.g. a host may repetitively invoke this function until the queue is + /// drained to implement the "microtask checkpint" defined in WHATWG HTML + /// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint. + /// + /// Note that error propagation is only a concern if a host needs to implement + /// `queueMicrotask`, a recent API that allows enqueueing aribitary functions + /// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are + /// handled internally to VMs and are never propagrated to hosts. + /// + /// This API offers some queue management to hosts at its best effort due to + /// different behaviors and limitations imposed by different VMs and APIs. By + /// the time this is written, An implementation may swallow exceptions (JSC), + /// may not pause (V8), and may not support bounded executions. + virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0; + /// \return the global object virtual Object global() = 0; @@ -995,8 +1024,8 @@ class JSI_EXPORT Value { return runtime.createValueFromJsonUtf8(json, length); } - /// \return according to the SameValue algorithm see more here: - // https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4 + /// \return according to the Strict Equality Comparison algorithm, see: + /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); Value& operator=(Value&& other) { diff --git a/android/ReactCommon/jsi/jsi/jsilib-posix.cpp b/android/ReactCommon/jsi/jsi/jsilib-posix.cpp index 3ed8d6cdfdb9a..87b1bdf364d34 100644 --- a/android/ReactCommon/jsi/jsi/jsilib-posix.cpp +++ b/android/ReactCommon/jsi/jsi/jsilib-posix.cpp @@ -18,15 +18,6 @@ #include -#if __APPLE__ -#include -#define MAP_TAG VM_MAKE_TAG(VM_MEMORY_APPLICATION_SPECIFIC_16) -#endif // __APPLE__ - -#ifndef MAP_TAG -#define MAP_TAG 0 -#endif - namespace facebook { namespace jsi { @@ -76,8 +67,7 @@ class ScopedFile { } uint8_t* mmap(size_t size) { - void* result = - ::mmap(nullptr, size, PROT_READ, MAP_PRIVATE | MAP_TAG, fd_, 0); + void* result = ::mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd_, 0); if (result == MAP_FAILED) { throwFormattedError( "Could not mmap %s: %s", path_.c_str(), strerror(errno)); diff --git a/android/ReactCommon/jsi/jsi/test/testlib.cpp b/android/ReactCommon/jsi/jsi/test/testlib.cpp index 8cf831d34f7b8..e605b6acead95 100644 --- a/android/ReactCommon/jsi/jsi/test/testlib.cpp +++ b/android/ReactCommon/jsi/jsi/test/testlib.cpp @@ -6,6 +6,7 @@ */ #include + #include #include #include diff --git a/android/ReactCommon/jsiexecutor/Android.mk b/android/ReactCommon/jsiexecutor/Android.mk index 74ae7a65cef20..e0661538e6664 100644 --- a/android/ReactCommon/jsiexecutor/Android.mk +++ b/android/ReactCommon/jsiexecutor/Android.mk @@ -16,7 +16,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_CFLAGS := -fexceptions -frtti -O3 -LOCAL_STATIC_LIBRARIES := libjsi reactnative reactperflogger -LOCAL_SHARED_LIBRARIES := libfolly_json glog +LOCAL_STATIC_LIBRARIES := reactnative reactperflogger +LOCAL_SHARED_LIBRARIES := libfolly_json glog libjsi include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactCommon/jsiexecutor/React-jsiexecutor.podspec b/android/ReactCommon/jsiexecutor/React-jsiexecutor.podspec index 45429c92bf325..1151d99ad2e38 100644 --- a/android/ReactCommon/jsiexecutor/React-jsiexecutor.podspec +++ b/android/ReactCommon/jsiexecutor/React-jsiexecutor.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,11 +27,11 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "jsireact/*.{cpp,h}" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "jsireact" s.dependency "React-cxxreact", version diff --git a/android/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/android/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index f490891446060..f1d16bacb399a 100644 --- a/android/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/android/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -7,6 +7,7 @@ #include "jsireact/JSIExecutor.h" +#include #include #include #include @@ -204,6 +205,30 @@ void JSIExecutor::registerBundle( ReactMarker::REGISTER_JS_SEGMENT_STOP, tag.c_str()); } +// Looping on \c drainMicrotasks until it completes or hits the retries bound. +static void performMicrotaskCheckpoint(jsi::Runtime &runtime) { + uint8_t retries = 0; + // A heuristic number to guard inifinite or absurd numbers of retries. + const static unsigned int kRetriesBound = 255; + + while (retries < kRetriesBound) { + try { + // The default behavior of \c drainMicrotasks is unbounded execution. + // We may want to make it bounded in the future. + if (runtime.drainMicrotasks()) { + break; + } + } catch (jsi::JSError &error) { + handleJSError(runtime, error, true); + } + retries++; + } + + if (retries == kRetriesBound) { + throw std::runtime_error("Hits microtasks retries bound."); + } +} + void JSIExecutor::callFunction( const std::string &moduleId, const std::string &methodId, @@ -240,6 +265,8 @@ void JSIExecutor::callFunction( std::runtime_error("Error calling " + moduleId + "." + methodId)); } + performMicrotaskCheckpoint(*runtime_); + callNativeModules(ret, true); } @@ -259,6 +286,8 @@ void JSIExecutor::invokeCallback( folly::to("Error invoking callback ", callbackId))); } + performMicrotaskCheckpoint(*runtime_); + callNativeModules(ret, true); } @@ -394,7 +423,9 @@ void JSIExecutor::callNativeModules(const Value &queue, bool isEndOfBatch) { void JSIExecutor::flush() { SystraceSection s("JSIExecutor::flush"); if (flushedQueue_) { - callNativeModules(flushedQueue_->call(*runtime_), true); + Value ret = flushedQueue_->call(*runtime_); + performMicrotaskCheckpoint(*runtime_); + callNativeModules(ret, true); return; } @@ -410,7 +441,9 @@ void JSIExecutor::flush() { // If calls were made, we bind to the JS bridge methods, and use them to // get the pending queue of native calls. bindBridge(); - callNativeModules(flushedQueue_->call(*runtime_), true); + Value ret = flushedQueue_->call(*runtime_); + performMicrotaskCheckpoint(*runtime_); + callNativeModules(ret, true); } else if (delegate_) { // If we have a delegate, we need to call it; we pass a null list to // callNativeModules, since we know there are no native calls, without @@ -439,7 +472,7 @@ Value JSIExecutor::nativeCallSyncHook(const Value *args, size_t count) { throw std::invalid_argument("nativeCallSyncHook arg count must be 3"); } - if (!args[2].asObject(*runtime_).isArray(*runtime_)) { + if (!args[2].isObject() || !args[2].asObject(*runtime_).isArray(*runtime_)) { throw std::invalid_argument( folly::to("method parameters should be array")); } diff --git a/android/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp b/android/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp index e7c56c206a5f2..0bf27d402e9ee 100644 --- a/android/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp +++ b/android/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp @@ -89,6 +89,8 @@ folly::Optional JSINativeModules::createModule( valueFromDynamic(rt, result->config), static_cast(result->index)); CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null"; + CHECK(moduleInfo.isObject()) + << "Module returned from genNativeModule isn't an Object"; folly::Optional module( moduleInfo.asObject(rt).getPropertyAsObject(rt, "module")); diff --git a/android/ReactCommon/jsinspector/BUCK b/android/ReactCommon/jsinspector/BUCK index a64c399b567d7..c298131a299c5 100644 --- a/android/ReactCommon/jsinspector/BUCK +++ b/android/ReactCommon/jsinspector/BUCK @@ -1,4 +1,5 @@ load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob") +load("@fbsource//tools/build_defs:platform_defs.bzl", "ANDROID", "APPLE", "CXX", "FBCODE", "WINDOWS") load("//tools/build_defs/oss:rn_defs.bzl", "rn_xplat_cxx_library") EXPORTED_HEADERS = [ @@ -25,13 +26,9 @@ rn_xplat_cxx_library( ], prefix = "jsinspector", ), - compiler_flags = [ - "-Wall", - "-fexceptions", - "-std=c++1y", - ], fbandroid_preferred_linkage = "shared", labels = ["supermodule:xplat/default/public.react_native.infra"], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), visibility = [ "PUBLIC", ], diff --git a/android/ReactCommon/jsinspector/React-jsinspector.podspec b/android/ReactCommon/jsinspector/React-jsinspector.podspec index f87cdeefe3aed..a29430de12e98 100644 --- a/android/ReactCommon/jsinspector/React-jsinspector.podspec +++ b/android/ReactCommon/jsinspector/React-jsinspector.podspec @@ -11,7 +11,7 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "*.{cpp,h}" s.header_dir = 'jsinspector' diff --git a/android/ReactCommon/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK b/android/ReactCommon/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK index 2a86bc6af780b..2a9f6a05bb329 100644 --- a/android/ReactCommon/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK +++ b/android/ReactCommon/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK @@ -3,6 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library", "rn_prebuilt_ja rn_android_library( name = "powermock2", + autoglob = False, visibility = ["PUBLIC"], exported_deps = [ ":javassist-prebuilt", @@ -20,6 +21,7 @@ rn_android_library( rn_android_library( name = "powermock-reflect", + autoglob = False, visibility = ["PUBLIC"], exported_deps = [ ":byte-buddy", diff --git a/android/ReactCommon/logger/Android.mk b/android/ReactCommon/logger/Android.mk new file mode 100644 index 0000000000000..09a29bd3e1585 --- /dev/null +++ b/android/ReactCommon/logger/Android.mk @@ -0,0 +1,23 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := logger + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) + +LOCAL_CFLAGS += -fexceptions + +LOCAL_SHARED_LIBRARIES := glog + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,glog) diff --git a/android/ReactCommon/logger/BUCK b/android/ReactCommon/logger/BUCK new file mode 100644 index 0000000000000..59976e3cb0643 --- /dev/null +++ b/android/ReactCommon/logger/BUCK @@ -0,0 +1,38 @@ +load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob") +load("@fbsource//tools/build_defs:platform_defs.bzl", "ANDROID", "APPLE", "CXX", "FBCODE", "WINDOWS") +load("//tools/build_defs/oss:rn_defs.bzl", "rn_xplat_cxx_library") + +EXPORTED_HEADERS = [ + "react_native_log.h", +] + +rn_xplat_cxx_library( + name = "logger", + srcs = glob( + ["*.cpp"], + ), + headers = subdir_glob( + [ + ("", "*.h"), + ], + exclude = EXPORTED_HEADERS, + prefix = "logger", + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", header) + for header in EXPORTED_HEADERS + ], + prefix = "logger", + ), + fbandroid_preferred_linkage = "shared", + labels = ["supermodule:xplat/default/public.react_native.infra"], + platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS), + visibility = [ + "PUBLIC", + ], + deps = [ + "//third-party/glog:glog", + ], +) diff --git a/android/ReactCommon/logger/React-logger.podspec b/android/ReactCommon/logger/React-logger.podspec new file mode 100644 index 0000000000000..39ae857e5e366 --- /dev/null +++ b/android/ReactCommon/logger/React-logger.podspec @@ -0,0 +1,40 @@ +# coding: utf-8 +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' +folly_version = '2021.06.28.00-v2' +boost_compiler_flags = '-Wno-documentation' + +Pod::Spec.new do |s| + s.name = "React-logger" + s.version = version + s.summary = "-" # TODO + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Facebook, Inc. and its affiliates" + s.platforms = { :ios => "11.0", :tvos => "11.0" } + s.source = source + s.source_files = "*.{cpp,h}" + s.exclude_files = "SampleCxxModule.*" + s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "" } + s.header_dir = "logger" + + s.dependency "glog" +end diff --git a/android/ReactCommon/logger/react_native_log.cpp b/android/ReactCommon/logger/react_native_log.cpp new file mode 100644 index 0000000000000..3b0310432c076 --- /dev/null +++ b/android/ReactCommon/logger/react_native_log.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "react_native_log.h" +#include + +static reactnativelogfunctype _reactnativelogfunc = NULL; + +void set_react_native_logfunc(reactnativelogfunctype newlogfunc) { + _reactnativelogfunc = newlogfunc; +} +void react_native_log_info(const char *message) { + _react_native_log(ReactNativeLogLevelInfo, message); +} +void react_native_log_warn(const char *message) { + _react_native_log(ReactNativeLogLevelWarning, message); +} +void react_native_log_error(const char *message) { + _react_native_log(ReactNativeLogLevelError, message); +} +void react_native_log_fatal(const char *message) { + _react_native_log(ReactNativeLogLevelFatal, message); +} + +void _react_native_log(ReactNativeLogLevel level, const char *message) { + if (_reactnativelogfunc == NULL) { + _react_native_log_default(level, message); + } else { + _reactnativelogfunc(level, message); + } +} + +void _react_native_log_default(ReactNativeLogLevel level, const char *message) { + switch (level) { + case ReactNativeLogLevelInfo: + LOG(INFO) << message; + break; + case ReactNativeLogLevelWarning: + LOG(WARNING) << message; + break; + case ReactNativeLogLevelError: + LOG(ERROR) << message; + break; + case ReactNativeLogLevelFatal: + LOG(FATAL) << message; + break; + } +} diff --git a/android/ReactCommon/logger/react_native_log.h b/android/ReactCommon/logger/react_native_log.h new file mode 100644 index 0000000000000..c64f202555980 --- /dev/null +++ b/android/ReactCommon/logger/react_native_log.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +enum ReactNativeLogLevel { + ReactNativeLogLevelInfo = 1, + ReactNativeLogLevelWarning = 2, + ReactNativeLogLevelError = 3, + ReactNativeLogLevelFatal = 4 +}; + +typedef void (*reactnativelogfunctype)(ReactNativeLogLevel, const char *); + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +void set_react_native_logfunc(reactnativelogfunctype newlogfunc); + +void react_native_log_info(const char *text); +void react_native_log_warn(const char *text); +void react_native_log_error(const char *text); +void react_native_log_fatal(const char *text); + +void _react_native_log(ReactNativeLogLevel level, const char *text); +void _react_native_log_default(ReactNativeLogLevel level, const char *text); +#ifdef __cplusplus +} +#endif // __cpusplus diff --git a/android/ReactCommon/react/config/Android.mk b/android/ReactCommon/react/config/Android.mk index ace2aa69ddf6d..c7b63c9699eba 100644 --- a/android/ReactCommon/react/config/Android.mk +++ b/android/ReactCommon/react/config/Android.mk @@ -17,7 +17,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := diff --git a/android/ReactCommon/react/config/BUCK b/android/ReactCommon/react/config/BUCK index 9640ee45c1a52..3fec8b7c1d5d2 100644 --- a/android/ReactCommon/react/config/BUCK +++ b/android/ReactCommon/react/config/BUCK @@ -18,12 +18,6 @@ rn_xplat_cxx_library( ], prefix = "react/config", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, diff --git a/android/ReactCommon/react/debug/Android.mk b/android/ReactCommon/react/debug/Android.mk new file mode 100644 index 0000000000000..8dcbe4f224a8e --- /dev/null +++ b/android/ReactCommon/react/debug/Android.mk @@ -0,0 +1,31 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_debug + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_SHARED_LIBRARIES := libfolly_json + +LOCAL_LDLIBS := -llog + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"Fabric\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall -llog + +LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,folly) diff --git a/android/ReactCommon/react/debug/BUCK b/android/ReactCommon/react/debug/BUCK new file mode 100644 index 0000000000000..603c6146e528a --- /dev/null +++ b/android/ReactCommon/react/debug/BUCK @@ -0,0 +1,63 @@ +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "debug", + srcs = glob( + ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), + ), + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/debug", + ), + exported_platform_linker_flags = [ + ( + "^android.*", + ["-llog"], + ), + ], + fbandroid_linker_flags = [ + # for android react_native_assert + "-llog", + ], + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + tests = [], + visibility = ["PUBLIC"], + deps = [ + "//xplat/fbsystrace:fbsystrace", + "//xplat/folly:headers_only", + "//xplat/folly:memory", + "//xplat/folly:molly", + ], + exported_deps = [ + "//third-party/glog:glog", + ], +) diff --git a/android/ReactCommon/react/debug/flags.h b/android/ReactCommon/react/debug/flags.h new file mode 100644 index 0000000000000..dc09ab36d40a3 --- /dev/null +++ b/android/ReactCommon/react/debug/flags.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +// +// Enable REACT_NATIVE_DEBUG if NDEBUG is not defined. +// Due to BUCK defaults in open-source, NDEBUG is always defined for all android +// builds (if you build without BUCK, this isn't an issue). Thus we introduce +// REACT_NATIVE_DEBUG that we use internally instead of NDEBUG that we can +// control and use as a more reliable xplat flag. For any build that doesn't +// have NDEBUG defined, we enable REACT_NATIVE_DEBUG for convenience. +#ifndef NDEBUG +#define REACT_NATIVE_DEBUG 1 +#endif diff --git a/android/ReactCommon/react/debug/react_native_assert.cpp b/android/ReactCommon/react/debug/react_native_assert.cpp new file mode 100644 index 0000000000000..a72b91aecc612 --- /dev/null +++ b/android/ReactCommon/react/debug/react_native_assert.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef __ANDROID__ + +#include + +// Provide a prototype to silence missing prototype warning in release +// mode. +extern "C" void react_native_assert_fail( + const char *func, + const char *file, + int line, + const char *expr); + +extern "C" void react_native_assert_fail( + const char *func, + const char *file, + int line, + const char *expr) { + // Print as an error so it shows up in logcat before crash.... + __android_log_print( + ANDROID_LOG_ERROR, + "ReactNative", + "%s:%d: function %s: assertion failed (%s)", + file, + line, + func, + expr); + // Print as a fatal so it crashes and shows up in uploaded logs + __android_log_print( + ANDROID_LOG_FATAL, + "ReactNative", + "%s:%d: function %s: assertion failed (%s)", + file, + line, + func, + expr); +} + +#endif // __ANDROID__ diff --git a/android/ReactCommon/react/debug/react_native_assert.h b/android/ReactCommon/react/debug/react_native_assert.h new file mode 100644 index 0000000000000..fc3f97b7bbfc6 --- /dev/null +++ b/android/ReactCommon/react/debug/react_native_assert.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// No header guards since it is legitimately possible to include this file more +// than once with and without REACT_NATIVE_DEBUG. + +// react_native_assert allows us to opt-in to specific asserts on Android and +// test before moving on. When all issues have been found, maybe we can use +// `UNDEBUG` flag to disable NDEBUG in debug builds on Android. + +#include "flags.h" + +#undef react_native_assert + +#ifndef REACT_NATIVE_DEBUG + +#define react_native_assert(e) ((void)0) + +#else // REACT_NATIVE_DEBUG + +#ifdef __ANDROID__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +void react_native_assert_fail( + const char *func, + const char *file, + int line, + const char *expr); +#ifdef __cplusplus +} +#endif // __cpusplus + +#define react_native_assert(e) \ + ((e) ? (void)0 : react_native_assert_fail(__func__, __FILE__, __LINE__, #e)) + +#else // __ANDROID__ + +#include +#include + +// For all platforms, but iOS+Xcode especially: flush logs because some might be +// lost on iOS if an assert is hit right after this. If you are trying to debug +// something actively and have added lots of LOG statements to track down an +// issue, there is race between flushing the final logs and stopping execution +// when the assert hits. Thus, if we know an assert will fail, we force flushing +// to happen right before the assert. +#define react_native_assert(cond) \ + if (!(cond)) { \ + LOG(ERROR) << "react_native_assert failure: " << #cond; \ + google::FlushLogFiles(google::INFO); \ + assert(cond); \ + } + +#endif // platforms besides __ANDROID__ + +#endif // REACT_NATIVE_DEBUG diff --git a/android/ReactCommon/react/nativemodule/core/Android.mk b/android/ReactCommon/react/nativemodule/core/Android.mk index 70f7651b4831f..bb2a0d79c7749 100644 --- a/android/ReactCommon/react/nativemodule/core/Android.mk +++ b/android/ReactCommon/react/nativemodule/core/Android.mk @@ -15,14 +15,14 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) $(wildcard $(LOCA LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/platform/android/ -LOCAL_SHARED_LIBRARIES := libfbjni libfolly_json libreactnativejni +LOCAL_SHARED_LIBRARIES := libfbjni libfolly_json libreactnativejni libreact_debug libjsi -LOCAL_STATIC_LIBRARIES := libjsi libreactperflogger +LOCAL_STATIC_LIBRARIES := libreactperflogger LOCAL_CFLAGS := \ -DLOG_TAG=\"ReactNative\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactCommon/react/nativemodule/core/BUCK b/android/ReactCommon/react/nativemodule/core/BUCK index d5c8981664981..b0e29b37c244a 100644 --- a/android/ReactCommon/react/nativemodule/core/BUCK +++ b/android/ReactCommon/react/nativemodule/core/BUCK @@ -1,4 +1,4 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", "OBJC_ARC_PREPROCESSOR_FLAGS", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags", "react_native_target", "react_native_xplat_shared_library_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob") +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", "get_objc_arc_preprocessor_flags", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags", "react_native_target", "react_native_xplat_shared_library_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob") rn_xplat_cxx_library( name = "core", @@ -13,10 +13,6 @@ rn_xplat_cxx_library( prefix = "ReactCommon", ), compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", "-Wno-global-constructors", ], fbandroid_deps = [ @@ -35,11 +31,10 @@ rn_xplat_cxx_library( ], ), fbobjc_compiler_flags = [ - "-Wall", "-fobjc-arc-exceptions", ], fbobjc_inherited_buck_flags = get_static_library_ios_flags(), - fbobjc_preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode(), + fbobjc_preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), ios_deps = [ "//xplat/FBBaseLite:FBBaseLite", "//xplat/js/react-native-github:RCTCxxModule", @@ -79,6 +74,7 @@ rn_xplat_cxx_library( react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("cxxreact:module"), react_native_xplat_target("callinvoker:callinvoker"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("reactperflogger:reactperflogger"), ], exported_deps = [ diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp b/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp index 34683aecb88ee..40908d3c07ecb 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp @@ -43,6 +43,7 @@ void LongLivedObjectCollection::clear() const { // LongLivedObject LongLivedObject::LongLivedObject() {} +LongLivedObject::~LongLivedObject() {} void LongLivedObject::allowRelease() { LongLivedObjectCollection::get().remove(this); diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h b/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h index 0f33b228fb633..5173b73d01623 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h @@ -26,10 +26,11 @@ namespace react { */ class LongLivedObject { public: - void allowRelease(); + virtual void allowRelease(); protected: LongLivedObject(); + virtual ~LongLivedObject(); }; /** @@ -39,6 +40,7 @@ class LongLivedObjectCollection { public: static LongLivedObjectCollection &get(); + LongLivedObjectCollection(); LongLivedObjectCollection(LongLivedObjectCollection const &) = delete; void operator=(LongLivedObjectCollection const &) = delete; @@ -47,7 +49,6 @@ class LongLivedObjectCollection { void clear() const; private: - LongLivedObjectCollection(); mutable std::unordered_set> collection_; mutable std::mutex collectionMutex_; }; diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp index fe1f57d2b45ab..f7045b3c89f50 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp @@ -32,7 +32,7 @@ jsi::Value TurboModule::get( return jsi::Function::createFromHostFunction( runtime, propName, - meta.argCount, + static_cast(meta.argCount), [this, meta]( facebook::jsi::Runtime &rt, const facebook::jsi::Value &thisVal, diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h index 8bc8362730ccf..0772986a76ab6 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h @@ -64,8 +64,8 @@ class JSI_EXPORT TurboModule : public facebook::jsi::HostObject { * An app/platform-specific provider function to get an instance of a module * given a name. */ -using TurboModuleProviderFunctionType = std::function(const std::string &name, const jsi::Value *schema)>; +using TurboModuleProviderFunctionType = + std::function(const std::string &name)>; } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp index 0b04ac69b10d6..9c339edf36b90 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp @@ -21,9 +21,19 @@ namespace react { /** * Public API to install the TurboModule system. */ + TurboModuleBinding::TurboModuleBinding( const TurboModuleProviderFunctionType &&moduleProvider) - : moduleProvider_(std::move(moduleProvider)) {} + : moduleProvider_(std::move(moduleProvider)), + longLivedObjectCollection_(nullptr), + disableGlobalLongLivedObjectCollection_(false) {} + +TurboModuleBinding::TurboModuleBinding( + const TurboModuleProviderFunctionType &&moduleProvider, + std::shared_ptr longLivedObjectCollection) + : moduleProvider_(std::move(moduleProvider)), + longLivedObjectCollection_(longLivedObjectCollection), + disableGlobalLongLivedObjectCollection_(true) {} void TurboModuleBinding::install( jsi::Runtime &runtime, @@ -35,6 +45,9 @@ void TurboModuleBinding::install( runtime, jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"), 1, + + // Create a TurboModuleBinding that uses the global + // LongLivedObjectCollection [binding = std::make_shared(std::move(moduleProvider))]( jsi::Runtime &rt, @@ -45,17 +58,48 @@ void TurboModuleBinding::install( })); } +void TurboModuleBinding::install( + jsi::Runtime &runtime, + const TurboModuleProviderFunctionType &&moduleProvider, + std::shared_ptr longLivedObjectCollection) { + runtime.global().setProperty( + runtime, + "__turboModuleProxy", + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"), + 1, + // Create a TurboModuleBinding that doesn't use the global + // LongLivedObjectCollection + [binding = std::make_shared( + std::move(moduleProvider), longLivedObjectCollection)]( + jsi::Runtime &rt, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count) { + return binding->jsProxy(rt, thisVal, args, count); + })); +} + TurboModuleBinding::~TurboModuleBinding() { + if (longLivedObjectCollection_ != nullptr) { + longLivedObjectCollection_->clear(); + return; + } + + if (disableGlobalLongLivedObjectCollection_) { + return; + } + LongLivedObjectCollection::get().clear(); } std::shared_ptr TurboModuleBinding::getModule( - const std::string &name, - const jsi::Value *schema) { + const std::string &name) { std::shared_ptr module = nullptr; { SystraceSection s("TurboModuleBinding::getModule", "module", name); - module = moduleProvider_(name, schema); + module = moduleProvider_(name); } return module; } @@ -72,10 +116,7 @@ jsi::Value TurboModuleBinding::jsProxy( std::string moduleName = args[0].getString(runtime).utf8(runtime); jsi::Value nullSchema = jsi::Value::undefined(); - std::shared_ptr module = - (count >= 2 ? getModule(moduleName, &args[1]) - : getModule(moduleName, &nullSchema)); - + std::shared_ptr module = getModule(moduleName); if (module == nullptr) { return jsi::Value::null(); } diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h index 2546730959fc1..87679af06bf29 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h @@ -9,6 +9,7 @@ #include +#include #include #include @@ -29,16 +30,21 @@ class TurboModuleBinding { static void install( jsi::Runtime &runtime, const TurboModuleProviderFunctionType &&moduleProvider); + static void install( + jsi::Runtime &runtime, + const TurboModuleProviderFunctionType &&moduleProvider, + std::shared_ptr longLivedObjectCollection); TurboModuleBinding(const TurboModuleProviderFunctionType &&moduleProvider); + TurboModuleBinding( + const TurboModuleProviderFunctionType &&moduleProvider, + std::shared_ptr longLivedObjectCollection); virtual ~TurboModuleBinding(); /** * Get an TurboModule instance for the given module name. */ - std::shared_ptr getModule( - const std::string &name, - const jsi::Value *schema); + std::shared_ptr getModule(const std::string &name); private: /** @@ -52,6 +58,8 @@ class TurboModuleBinding { size_t count); TurboModuleProviderFunctionType moduleProvider_; + std::shared_ptr longLivedObjectCollection_; + bool disableGlobalLongLivedObjectCollection_; }; } // namespace react diff --git a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h index 30add1d265baa..0dc08136a610d 100644 --- a/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h +++ b/android/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h @@ -47,10 +47,25 @@ class CallbackWrapper : public LongLivedObject { jsi::Function &&callback, jsi::Runtime &runtime, std::shared_ptr jsInvoker) - : callback_(std::move(callback)), + : longLivedObjectCollection_(), + callback_(std::move(callback)), runtime_(runtime), jsInvoker_(std::move(jsInvoker)) {} + CallbackWrapper( + std::shared_ptr longLivedObjectCollection, + jsi::Function &&callback, + jsi::Runtime &runtime, + std::shared_ptr jsInvoker) + : longLivedObjectCollection_(longLivedObjectCollection), + callback_(std::move(callback)), + runtime_(runtime), + jsInvoker_(std::move(jsInvoker)) {} + + // Use a weak_ptr to avoid a retain cycle: LongLivedObjectCollection owns all + // CallbackWrappers. So, CallbackWrapper cannot own its + // LongLivedObjectCollection. + std::weak_ptr longLivedObjectCollection_; jsi::Function callback_; jsi::Runtime &runtime_; std::shared_ptr jsInvoker_; @@ -66,6 +81,17 @@ class CallbackWrapper : public LongLivedObject { return wrapper; } + static std::weak_ptr createWeak( + std::shared_ptr longLivedObjectCollection, + jsi::Function &&callback, + jsi::Runtime &runtime, + std::shared_ptr jsInvoker) { + auto wrapper = std::shared_ptr(new CallbackWrapper( + longLivedObjectCollection, std::move(callback), runtime, jsInvoker)); + longLivedObjectCollection->add(wrapper); + return wrapper; + } + // Delete the enclosed jsi::Function void destroy() { allowRelease(); @@ -82,6 +108,34 @@ class CallbackWrapper : public LongLivedObject { CallInvoker &jsInvoker() { return *(jsInvoker_); } + + void allowRelease() override { + if (auto longLivedObjectCollection = longLivedObjectCollection_.lock()) { + if (longLivedObjectCollection != nullptr) { + longLivedObjectCollection->remove(this); + return; + } + } + LongLivedObject::allowRelease(); + } +}; + +class RAIICallbackWrapperDestroyer { + public: + RAIICallbackWrapperDestroyer(std::weak_ptr callbackWrapper) + : callbackWrapper_(callbackWrapper) {} + + ~RAIICallbackWrapperDestroyer() { + auto strongWrapper = callbackWrapper_.lock(); + if (!strongWrapper) { + return; + } + + strongWrapper->destroy(); + } + + private: + std::weak_ptr callbackWrapper_; }; } // namespace react diff --git a/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp b/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp index f4b3f967e91c4..18abcc3085b3b 100644 --- a/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +++ b/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,8 @@ namespace react { JavaTurboModule::JavaTurboModule(const InitParams ¶ms) : TurboModule(params.moduleName, params.jsInvoker), instance_(jni::make_global(params.instance)), - nativeInvoker_(params.nativeInvoker) {} + nativeInvoker_(params.nativeInvoker), + retainJSCallback_(params.retainJSCallback) {} JavaTurboModule::~JavaTurboModule() { /** @@ -51,22 +53,28 @@ JavaTurboModule::~JavaTurboModule() { }); } -bool JavaTurboModule::isPromiseAsyncDispatchEnabled_ = false; -void JavaTurboModule::enablePromiseAsyncDispatch(bool enable) { - isPromiseAsyncDispatchEnabled_ = enable; -} - namespace { jni::local_ref createJavaCallbackFromJSIFunction( + JSCallbackRetainer retainJSCallback, jsi::Function &&function, jsi::Runtime &rt, std::shared_ptr jsInvoker) { - auto weakWrapper = - react::CallbackWrapper::createWeak(std::move(function), rt, jsInvoker); + auto weakWrapper = retainJSCallback != nullptr + ? retainJSCallback(std::move(function), rt, jsInvoker) + : react::CallbackWrapper::createWeak(std::move(function), rt, jsInvoker); + + // This needs to be a shared_ptr because: + // 1. It cannot be unique_ptr. std::function is copyable but unique_ptr is + // not. + // 2. It cannot be weak_ptr since we need this object to live on. + // 3. It cannot be a value, because that would be deleted as soon as this + // function returns. + auto callbackWrapperOwner = + std::make_shared(weakWrapper); std::function fn = - [weakWrapper, - wrapperWasCalled = false](folly::dynamic responses) mutable { + [weakWrapper, callbackWrapperOwner, wrapperWasCalled = false]( + folly::dynamic responses) mutable { if (wrapperWasCalled) { throw std::runtime_error( "callback 2 arg cannot be called more than once"); @@ -77,35 +85,37 @@ jni::local_ref createJavaCallbackFromJSIFunction( return; } - strongWrapper->jsInvoker().invokeAsync([weakWrapper, responses]() { - auto strongWrapper2 = weakWrapper.lock(); - if (!strongWrapper2) { - return; - } - - // TODO (T43155926) valueFromDynamic already returns a Value array. - // Don't iterate again - jsi::Value args = - jsi::valueFromDynamic(strongWrapper2->runtime(), responses); - auto argsArray = args.getObject(strongWrapper2->runtime()) - .asArray(strongWrapper2->runtime()); - std::vector result; - for (size_t i = 0; i < argsArray.size(strongWrapper2->runtime()); - i++) { - result.emplace_back( - strongWrapper2->runtime(), - argsArray.getValueAtIndex(strongWrapper2->runtime(), i)); - } - strongWrapper2->callback().call( - strongWrapper2->runtime(), - (const jsi::Value *)result.data(), - result.size()); - - strongWrapper2->destroy(); - }); + strongWrapper->jsInvoker().invokeAsync( + [weakWrapper, callbackWrapperOwner, responses]() mutable { + auto strongWrapper2 = weakWrapper.lock(); + if (!strongWrapper2) { + return; + } + + // TODO (T43155926) valueFromDynamic already returns a Value + // array. Don't iterate again + jsi::Value args = + jsi::valueFromDynamic(strongWrapper2->runtime(), responses); + auto argsArray = args.getObject(strongWrapper2->runtime()) + .asArray(strongWrapper2->runtime()); + std::vector result; + for (size_t i = 0; i < argsArray.size(strongWrapper2->runtime()); + i++) { + result.emplace_back( + strongWrapper2->runtime(), + argsArray.getValueAtIndex(strongWrapper2->runtime(), i)); + } + strongWrapper2->callback().call( + strongWrapper2->runtime(), + (const jsi::Value *)result.data(), + result.size()); + + callbackWrapperOwner.reset(); + }); wrapperWasCalled = true; }; + return JCxxCallbackImpl::newObjectCxxArgs(fn); } @@ -138,7 +148,7 @@ std::string stringifyJSIValue(const jsi::Value &v, jsi::Runtime *rt = nullptr) { return "a string (\"" + v.getString(*rt).utf8(*rt) + "\")"; } - assert(v.isObject() && "Expecting object."); + react_native_assert(v.isObject() && "Expecting object."); return rt != nullptr && v.getObject(*rt).isFunction(*rt) ? "a function" : "an object"; } @@ -244,7 +254,8 @@ JNIArgs JavaTurboModule::convertJSIArgsToJNIArgs( const jsi::Value *args, size_t count, std::shared_ptr jsInvoker, - TurboModuleMethodValueKind valueKind) { + TurboModuleMethodValueKind valueKind, + JSCallbackRetainer retainJSCallback) { unsigned int expectedArgumentCount = valueKind == PromiseKind ? methodArgTypes.size() - 1 : methodArgTypes.size(); @@ -260,8 +271,7 @@ JNIArgs JavaTurboModule::convertJSIArgsToJNIArgs( auto makeGlobalIfNecessary = [&globalRefs, env, valueKind](jobject obj) -> jobject { - if (valueKind == VoidKind || - (valueKind == PromiseKind && isPromiseAsyncDispatchEnabled_)) { + if (valueKind == VoidKind || valueKind == PromiseKind) { jobject globalObj = env->NewGlobalRef(obj); globalRefs.push_back(globalObj); env->DeleteLocalRef(obj); @@ -380,7 +390,8 @@ JNIArgs JavaTurboModule::convertJSIArgsToJNIArgs( jsi::Function fn = arg->getObject(rt).getFunction(rt); jarg->l = makeGlobalIfNecessary( - createJavaCallbackFromJSIFunction(std::move(fn), rt, jsInvoker) + createJavaCallbackFromJSIFunction( + retainJSCallback, std::move(fn), rt, jsInvoker) .release()); continue; } @@ -429,9 +440,7 @@ jsi::Value JavaTurboModule::invokeJavaMethod( const char *methodName = methodNameStr.c_str(); const char *moduleName = name_.c_str(); - bool isMethodSync = - !(valueKind == VoidKind || - (valueKind == PromiseKind && isPromiseAsyncDispatchEnabled_)); + bool isMethodSync = !(valueKind == VoidKind || valueKind == PromiseKind); if (isMethodSync) { TMPL::syncMethodCallStart(moduleName, methodName); @@ -529,7 +538,8 @@ jsi::Value JavaTurboModule::invokeJavaMethod( args, argCount, jsInvoker_, - valueKind); + valueKind, + retainJSCallback_); if (isMethodSync && valueKind != PromiseKind) { TMPL::syncMethodCallArgConversionEnd(moduleName, methodName); @@ -690,10 +700,14 @@ jsi::Value JavaTurboModule::invokeJavaMethod( [jargs, globalRefs, methodID, - instance_ = instance_, + instance_ = jni::make_weak(instance_), moduleNameStr = name_, methodNameStr, id = getUniqueId()]() mutable -> void { + auto instance = instance_.lockLocal(); + if (!instance) { + return; + } /** * TODO(ramanpreet): Why do we have to require the environment * again? Why does JNI crash when we use the env from the upper @@ -704,7 +718,7 @@ jsi::Value JavaTurboModule::invokeJavaMethod( const char *methodName = methodNameStr.c_str(); TMPL::asyncMethodCallExecutionStart(moduleName, methodName, id); - env->CallVoidMethodA(instance_.get(), methodID, jargs.data()); + env->CallVoidMethodA(instance.get(), methodID, jargs.data()); try { FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); } catch (...) { @@ -736,7 +750,8 @@ jsi::Value JavaTurboModule::invokeJavaMethod( methodID, moduleNameStr = name_, methodNameStr, - env]( + env, + retainJSCallback = retainJSCallback_]( jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *promiseConstructorArgs, @@ -753,10 +768,16 @@ jsi::Value JavaTurboModule::invokeJavaMethod( runtime); auto resolve = createJavaCallbackFromJSIFunction( - std::move(resolveJSIFn), runtime, jsInvoker_) + retainJSCallback, + std::move(resolveJSIFn), + runtime, + jsInvoker_) .release(); auto reject = createJavaCallbackFromJSIFunction( - std::move(rejectJSIFn), runtime, jsInvoker_) + retainJSCallback, + std::move(rejectJSIFn), + runtime, + jsInvoker_) .release(); jclass jPromiseImpl = @@ -772,60 +793,53 @@ jsi::Value JavaTurboModule::invokeJavaMethod( const char *moduleName = moduleNameStr.c_str(); const char *methodName = methodNameStr.c_str(); - if (isPromiseAsyncDispatchEnabled_) { - jobject globalPromise = env->NewGlobalRef(promise); - - globalRefs.push_back(globalPromise); - env->DeleteLocalRef(promise); - - jargs[argCount].l = globalPromise; - TMPL::asyncMethodCallArgConversionEnd(moduleName, methodName); - TMPL::asyncMethodCallDispatch(moduleName, methodName); - - nativeInvoker_->invokeAsync( - [jargs, - globalRefs, - methodID, - instance_ = instance_, - moduleNameStr, - methodNameStr, - id = getUniqueId()]() mutable -> void { - /** - * TODO(ramanpreet): Why do we have to require the - * environment again? Why does JNI crash when we use the env - * from the upper scope? - */ - JNIEnv *env = jni::Environment::current(); - const char *moduleName = moduleNameStr.c_str(); - const char *methodName = methodNameStr.c_str(); - - TMPL::asyncMethodCallExecutionStart( - moduleName, methodName, id); - env->CallVoidMethodA( - instance_.get(), methodID, jargs.data()); - try { - FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); - } catch (...) { - TMPL::asyncMethodCallExecutionFail( - moduleName, methodName, id); - throw; - } - - for (auto globalRef : globalRefs) { - env->DeleteGlobalRef(globalRef); - } - TMPL::asyncMethodCallExecutionEnd( + jobject globalPromise = env->NewGlobalRef(promise); + + globalRefs.push_back(globalPromise); + env->DeleteLocalRef(promise); + + jargs[argCount].l = globalPromise; + TMPL::asyncMethodCallArgConversionEnd(moduleName, methodName); + TMPL::asyncMethodCallDispatch(moduleName, methodName); + + nativeInvoker_->invokeAsync( + [jargs, + globalRefs, + methodID, + instance_ = jni::make_weak(instance_), + moduleNameStr, + methodNameStr, + id = getUniqueId()]() mutable -> void { + auto instance = instance_.lockLocal(); + + if (!instance) { + return; + } + /** + * TODO(ramanpreet): Why do we have to require the + * environment again? Why does JNI crash when we use the env + * from the upper scope? + */ + JNIEnv *env = jni::Environment::current(); + const char *moduleName = moduleNameStr.c_str(); + const char *methodName = methodNameStr.c_str(); + + TMPL::asyncMethodCallExecutionStart( + moduleName, methodName, id); + env->CallVoidMethodA(instance.get(), methodID, jargs.data()); + try { + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + } catch (...) { + TMPL::asyncMethodCallExecutionFail( moduleName, methodName, id); - }); - - } else { - jargs[argCount].l = promise; - TMPL::syncMethodCallArgConversionEnd(moduleName, methodName); - TMPL::syncMethodCallExecutionStart(moduleName, methodName); - env->CallVoidMethodA(instance_.get(), methodID, jargs.data()); - TMPL::syncMethodCallExecutionEnd(moduleName, methodName); - TMPL::syncMethodCallReturnConversionStart(moduleName, methodName); - } + throw; + } + + for (auto globalRef : globalRefs) { + env->DeleteGlobalRef(globalRef); + } + TMPL::asyncMethodCallExecutionEnd(moduleName, methodName, id); + }); return jsi::Value::undefined(); }); @@ -834,12 +848,8 @@ jsi::Value JavaTurboModule::invokeJavaMethod( Promise.callAsConstructor(runtime, promiseConstructorArg); checkJNIErrorForMethodCall(); - if (isPromiseAsyncDispatchEnabled_) { - TMPL::asyncMethodCallEnd(moduleName, methodName); - } else { - TMPL::syncMethodCallReturnConversionEnd(moduleName, methodName); - TMPL::syncMethodCallEnd(moduleName, methodName); - } + TMPL::asyncMethodCallEnd(moduleName, methodName); + return promise; } default: diff --git a/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h b/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h index 93acda48fcff5..4c5975844763e 100644 --- a/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h +++ b/android/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h @@ -7,9 +7,11 @@ #pragma once +#include #include #include +#include #include #include #include @@ -30,6 +32,11 @@ struct JTurboModule : jni::JavaClass { "Lcom/facebook/react/turbomodule/core/interfaces/TurboModule;"; }; +using JSCallbackRetainer = std::function( + jsi::Function &&callback, + jsi::Runtime &runtime, + std::shared_ptr jsInvoker)>; + class JSI_EXPORT JavaTurboModule : public TurboModule { public: // TODO(T65603471): Should we unify this with a Fabric abstraction? @@ -38,10 +45,12 @@ class JSI_EXPORT JavaTurboModule : public TurboModule { jni::alias_ref instance; std::shared_ptr jsInvoker; std::shared_ptr nativeInvoker; + JSCallbackRetainer retainJSCallback; }; JavaTurboModule(const InitParams ¶ms); virtual ~JavaTurboModule(); + jsi::Value invokeJavaMethod( jsi::Runtime &runtime, TurboModuleMethodValueKind valueKind, @@ -50,16 +59,10 @@ class JSI_EXPORT JavaTurboModule : public TurboModule { const jsi::Value *args, size_t argCount); - static void enablePromiseAsyncDispatch(bool enable); - private: jni::global_ref instance_; std::shared_ptr nativeInvoker_; - - /** - * Experiments - */ - static bool isPromiseAsyncDispatchEnabled_; + JSCallbackRetainer retainJSCallback_; JNIArgs convertJSIArgsToJNIArgs( JNIEnv *env, @@ -69,7 +72,8 @@ class JSI_EXPORT JavaTurboModule : public TurboModule { const jsi::Value *args, size_t count, std::shared_ptr jsInvoker, - TurboModuleMethodValueKind valueKind); + TurboModuleMethodValueKind valueKind, + JSCallbackRetainer retainJSCallbacks); }; } // namespace react diff --git a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.h b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.h new file mode 100644 index 0000000000000..38dbd2bee4b7f --- /dev/null +++ b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * RCTBlockGuard is designed to be used with obj-c blocks to assist with manual deallocation of C++ resources + * tied to lifetime of a block. If C++ resources needs to be manually released at the end of block or when the block + * is deallocated, place the clean up code inside constructor and make sure the instace of the class is references in + * the block. + */ +@interface RCTBlockGuard : NSObject + +- (instancetype)initWithCleanup:(void (^)(void))cleanup; + +@end + +NS_ASSUME_NONNULL_END diff --git a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.mm b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.mm new file mode 100644 index 0000000000000..54a856a25a768 --- /dev/null +++ b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTBlockGuard.mm @@ -0,0 +1,28 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTBlockGuard.h" + +@implementation RCTBlockGuard { + void (^_cleanup)(void); +} + +- (instancetype)initWithCleanup:(void (^)(void))cleanup +{ + if (self = [super init]) { + _cleanup = cleanup; + } + + return self; +} + +- (void)dealloc +{ + _cleanup(); +} + +@end diff --git a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h index bdd9449468928..1cc512c992c2c 100644 --- a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h +++ b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h @@ -29,6 +29,9 @@ namespace react { class Instance; +typedef std::weak_ptr ( + ^RCTRetainJSCallback)(jsi::Function &&callback, jsi::Runtime &runtime, std::shared_ptr jsInvoker); + /** * ObjC++ specific TurboModule base class. */ @@ -41,6 +44,7 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { std::shared_ptr jsInvoker; std::shared_ptr nativeInvoker; bool isSyncModule; + RCTRetainJSCallback retainJSCallback; }; ObjCTurboModule(const InitParams ¶ms); @@ -63,6 +67,8 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { // Does the NativeModule dispatch async methods to the JS thread? const bool isSyncModule_; + RCTRetainJSCallback retainJSCallback_; + /** * TODO(ramanpreet): * Investigate an optimization that'll let us get rid of this NSMutableDictionary. @@ -90,29 +96,15 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { NSMutableArray *retainedObjectsForInvocation); using PromiseInvocationBlock = void (^)(RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper); - jsi::Value - createPromise(jsi::Runtime &runtime, std::shared_ptr jsInvoker, PromiseInvocationBlock invoke); + jsi::Value createPromise(jsi::Runtime &runtime, std::string methodName, PromiseInvocationBlock invoke); }; } // namespace react } // namespace facebook @protocol RCTTurboModule -@optional -/** - * Used by TurboModules to get access to other TurboModules. - * - * Usage: - * Place `@synthesize turboModuleRegistry = _turboModuleRegistry` - * in the @implementation section of your TurboModule. - */ -@property (nonatomic, weak) id turboModuleRegistry; - -@optional -// This should be required, after migration is done. - (std::shared_ptr)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params; - @end /** diff --git a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm index 4c7d3031bfb44..c6e53547a65e1 100644 --- a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm +++ b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm @@ -6,10 +6,12 @@ */ #import "RCTTurboModule.h" +#import "RCTBlockGuard.h" #import #import #import +#import #import #import @@ -98,28 +100,38 @@ static int32_t getUniqueId() return jsi::Value::undefined(); } -static id -convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr jsInvoker); +static id convertJSIValueToObjCObject( + jsi::Runtime &runtime, + const jsi::Value &value, + std::shared_ptr jsInvoker, + RCTRetainJSCallback retainJSCallback); static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::String &value) { return [NSString stringWithUTF8String:value.utf8(runtime).c_str()]; } -static NSArray * -convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr jsInvoker) +static NSArray *convertJSIArrayToNSArray( + jsi::Runtime &runtime, + const jsi::Array &value, + std::shared_ptr jsInvoker, + RCTRetainJSCallback retainJSCallback) { size_t size = value.size(runtime); NSMutableArray *result = [NSMutableArray new]; for (size_t i = 0; i < size; i++) { // Insert kCFNull when it's `undefined` value to preserve the indices. [result - addObject:convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker) ?: (id)kCFNull]; + addObject:convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker, retainJSCallback) + ?: (id)kCFNull]; } return [result copy]; } -static NSDictionary * -convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr jsInvoker) +static NSDictionary *convertJSIObjectToNSDictionary( + jsi::Runtime &runtime, + const jsi::Object &value, + std::shared_ptr jsInvoker, + RCTRetainJSCallback retainJSCallback) { jsi::Array propertyNames = value.getPropertyNames(runtime); size_t size = propertyNames.size(runtime); @@ -127,7 +139,7 @@ static int32_t getUniqueId() for (size_t i = 0; i < size; i++) { jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime); NSString *k = convertJSIStringToNSString(runtime, name); - id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker); + id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker, retainJSCallback); if (v) { result[k] = v; } @@ -135,10 +147,16 @@ static int32_t getUniqueId() return [result copy]; } -static RCTResponseSenderBlock -convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value, std::shared_ptr jsInvoker); -static id -convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr jsInvoker) +static RCTResponseSenderBlock convertJSIFunctionToCallback( + jsi::Runtime &runtime, + const jsi::Function &value, + std::shared_ptr jsInvoker, + RCTRetainJSCallback retainJSCallback); +static id convertJSIValueToObjCObject( + jsi::Runtime &runtime, + const jsi::Value &value, + std::shared_ptr jsInvoker, + RCTRetainJSCallback retainJSCallback) { if (value.isUndefined() || value.isNull()) { return nil; @@ -155,21 +173,33 @@ static int32_t getUniqueId() if (value.isObject()) { jsi::Object o = value.getObject(runtime); if (o.isArray(runtime)) { - return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker); + return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker, retainJSCallback); } if (o.isFunction(runtime)) { - return convertJSIFunctionToCallback(runtime, std::move(o.getFunction(runtime)), jsInvoker); + return convertJSIFunctionToCallback(runtime, std::move(o.getFunction(runtime)), jsInvoker, retainJSCallback); } - return convertJSIObjectToNSDictionary(runtime, o, jsInvoker); + return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, retainJSCallback); } throw std::runtime_error("Unsupported jsi::jsi::Value kind"); } -static RCTResponseSenderBlock -convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value, std::shared_ptr jsInvoker) +static RCTResponseSenderBlock convertJSIFunctionToCallback( + jsi::Runtime &runtime, + const jsi::Function &value, + std::shared_ptr jsInvoker, + RCTRetainJSCallback retainJSCallback) { - auto weakWrapper = CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker); + auto weakWrapper = retainJSCallback != nil + ? retainJSCallback(value.getFunction(runtime), runtime, jsInvoker) + : CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker); + RCTBlockGuard *blockGuard = [[RCTBlockGuard alloc] initWithCleanup:^() { + auto strongWrapper = weakWrapper.lock(); + if (strongWrapper) { + strongWrapper->destroy(); + } + }]; + BOOL __block wrapperWasCalled = NO; RCTResponseSenderBlock callback = ^(NSArray *responses) { if (wrapperWasCalled) { @@ -181,7 +211,7 @@ static int32_t getUniqueId() return; } - strongWrapper->jsInvoker().invokeAsync([weakWrapper, responses]() { + strongWrapper->jsInvoker().invokeAsync([weakWrapper, responses, blockGuard]() { auto strongWrapper2 = weakWrapper.lock(); if (!strongWrapper2) { return; @@ -190,31 +220,28 @@ static int32_t getUniqueId() std::vector args = convertNSArrayToStdVector(strongWrapper2->runtime(), responses); strongWrapper2->callback().call(strongWrapper2->runtime(), (const jsi::Value *)args.data(), args.size()); strongWrapper2->destroy(); + + // Delete the CallbackWrapper when the block gets dealloced without being invoked. + (void)blockGuard; }); wrapperWasCalled = YES; }; - if (RCTTurboModuleBlockCopyEnabled()) { - return [callback copy]; - } - - return callback; + return [callback copy]; } namespace facebook { namespace react { -jsi::Value ObjCTurboModule::createPromise( - jsi::Runtime &runtime, - std::shared_ptr jsInvoker, - PromiseInvocationBlock invoke) +jsi::Value ObjCTurboModule::createPromise(jsi::Runtime &runtime, std::string methodName, PromiseInvocationBlock invoke) { if (!invoke) { return jsi::Value::undefined(); } jsi::Function Promise = runtime.global().getPropertyAsFunction(runtime, "Promise"); + std::string moduleName = name_; // Note: the passed invoke() block is not retained by default, so let's retain it here to help keep it longer. // Otherwise, there's a risk of it getting released before the promise function below executes. @@ -223,28 +250,50 @@ static int32_t getUniqueId() runtime, jsi::PropNameID::forAscii(runtime, "fn"), 2, - [invokeCopy, jsInvoker](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) { + [invokeCopy, jsInvoker = jsInvoker_, moduleName, methodName, retainJSCallback = retainJSCallback_]( + jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) { + std::string moduleMethod = moduleName + "." + methodName + "()"; + if (count != 2) { throw std::invalid_argument( - "Promise must pass constructor function two args. Passed " + std::to_string(count) + " args."); + moduleMethod + ": Promise must pass constructor function two args. Passed " + std::to_string(count) + + " args."); } if (!invokeCopy) { return jsi::Value::undefined(); } - auto weakResolveWrapper = CallbackWrapper::createWeak(args[0].getObject(rt).getFunction(rt), rt, jsInvoker); - auto weakRejectWrapper = CallbackWrapper::createWeak(args[1].getObject(rt).getFunction(rt), rt, jsInvoker); + auto weakResolveWrapper = retainJSCallback != nil + ? retainJSCallback(args[0].getObject(rt).getFunction(rt), rt, jsInvoker) + : CallbackWrapper::createWeak(args[0].getObject(rt).getFunction(rt), rt, jsInvoker); + auto weakRejectWrapper = retainJSCallback != nil + ? retainJSCallback(args[1].getObject(rt).getFunction(rt), rt, jsInvoker) + : CallbackWrapper::createWeak(args[1].getObject(rt).getFunction(rt), rt, jsInvoker); __block BOOL resolveWasCalled = NO; __block BOOL rejectWasCalled = NO; + RCTBlockGuard *blockGuard = [[RCTBlockGuard alloc] initWithCleanup:^() { + auto strongResolveWrapper = weakResolveWrapper.lock(); + if (strongResolveWrapper) { + strongResolveWrapper->destroy(); + } + + auto strongRejectWrapper = weakRejectWrapper.lock(); + if (strongRejectWrapper) { + strongRejectWrapper->destroy(); + } + }]; + RCTPromiseResolveBlock resolveBlock = ^(id result) { if (rejectWasCalled) { - throw std::runtime_error("Tried to resolve a promise after it's already been rejected."); + RCTLogError(@"%s: Tried to resolve a promise after it's already been rejected.", moduleMethod.c_str()); + return; } if (resolveWasCalled) { - throw std::runtime_error("Tried to resolve a promise more than once."); + RCTLogError(@"%s: Tried to resolve a promise more than once.", moduleMethod.c_str()); + return; } auto strongResolveWrapper = weakResolveWrapper.lock(); @@ -253,7 +302,7 @@ static int32_t getUniqueId() return; } - strongResolveWrapper->jsInvoker().invokeAsync([weakResolveWrapper, weakRejectWrapper, result]() { + strongResolveWrapper->jsInvoker().invokeAsync([weakResolveWrapper, weakRejectWrapper, result, blockGuard]() { auto strongResolveWrapper2 = weakResolveWrapper.lock(); auto strongRejectWrapper2 = weakRejectWrapper.lock(); if (!strongResolveWrapper2 || !strongRejectWrapper2) { @@ -266,6 +315,7 @@ static int32_t getUniqueId() strongResolveWrapper2->destroy(); strongRejectWrapper2->destroy(); + (void)blockGuard; }); resolveWasCalled = YES; @@ -273,11 +323,13 @@ static int32_t getUniqueId() RCTPromiseRejectBlock rejectBlock = ^(NSString *code, NSString *message, NSError *error) { if (resolveWasCalled) { - throw std::runtime_error("Tried to reject a promise after it's already been resolved."); + RCTLogError(@"%s: Tried to reject a promise after it's already been resolved.", moduleMethod.c_str()); + return; } if (rejectWasCalled) { - throw std::runtime_error("Tried to reject a promise more than once."); + RCTLogError(@"%s: Tried to reject a promise more than once.", moduleMethod.c_str()); + return; } auto strongResolveWrapper = weakResolveWrapper.lock(); @@ -287,7 +339,7 @@ static int32_t getUniqueId() } NSDictionary *jsError = RCTJSErrorFromCodeMessageAndNSError(code, message, error); - strongRejectWrapper->jsInvoker().invokeAsync([weakResolveWrapper, weakRejectWrapper, jsError]() { + strongRejectWrapper->jsInvoker().invokeAsync([weakResolveWrapper, weakRejectWrapper, jsError, blockGuard]() { auto strongResolveWrapper2 = weakResolveWrapper.lock(); auto strongRejectWrapper2 = weakRejectWrapper.lock(); if (!strongResolveWrapper2 || !strongRejectWrapper2) { @@ -300,6 +352,7 @@ static int32_t getUniqueId() strongResolveWrapper2->destroy(); strongRejectWrapper2->destroy(); + (void)blockGuard; }); rejectWasCalled = YES; @@ -538,7 +591,7 @@ static int32_t getUniqueId() /** * Convert arg to ObjC objects. */ - id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker_); + id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker_, retainJSCallback_); if (objCArg) { NSString *methodNameNSString = @(methodName); @@ -610,7 +663,8 @@ static int32_t getUniqueId() : TurboModule(params.moduleName, params.jsInvoker), instance_(params.instance), nativeInvoker_(params.nativeInvoker), - isSyncModule_(params.isSyncModule) + isSyncModule_(params.isSyncModule), + retainJSCallback_(params.retainJSCallback) { } @@ -638,15 +692,10 @@ static int32_t getUniqueId() jsi::Value returnValue = returnType == PromiseKind ? createPromise( runtime, - jsInvoker_, + methodNameStr, ^(RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) { - RCTPromiseResolveBlock resolveCopy = resolveBlock; - RCTPromiseRejectBlock rejectCopy = rejectBlock; - - if (RCTTurboModuleBlockCopyEnabled()) { - resolveCopy = [resolveBlock copy]; - rejectCopy = [rejectBlock copy]; - } + RCTPromiseResolveBlock resolveCopy = [resolveBlock copy]; + RCTPromiseRejectBlock rejectCopy = [rejectBlock copy]; [inv setArgument:(void *)&resolveCopy atIndex:count + 2]; [inv setArgument:(void *)&rejectCopy atIndex:count + 3]; diff --git a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h index 8ee2eaeefe582..2b9c473194017 100644 --- a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h +++ b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h @@ -15,16 +15,10 @@ @protocol RCTTurboModuleManagerDelegate -// TODO: Move to xplat codegen. -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams: - (const facebook::react::ObjCTurboModule::InitParams &)params; @optional - (NSArray *)getEagerInitModuleNames; - (NSArray *)getEagerInitMainQueueModuleNames; -@optional - /** * Given a module name, return its actual class. If not provided, basic ObjC class lookup is performed. */ diff --git a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm index 223522eb684f4..a63c805609de6 100644 --- a/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm +++ b/android/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm @@ -18,6 +18,7 @@ #import #import #import +#import #import #import #import @@ -172,6 +173,9 @@ @implementation RCTTurboModuleManager { std::shared_timed_mutex _turboModuleHoldersSharedMutex; std::mutex _turboModuleHoldersMutex; std::atomic _invalidating; + + RCTRetainJSCallback _retainJSCallback; + std::shared_ptr _longLivedObjectCollection; } - (instancetype)initWithBridge:(RCTBridge *)bridge @@ -192,6 +196,23 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge selector:@selector(bridgeDidInvalidateModules:) name:RCTBridgeDidInvalidateModulesNotification object:_bridge.parentBridge]; + + if (RCTGetTurboModuleCleanupMode() == kRCTGlobalScope) { + // Use LongLivedObjectCollection singleton regularly + _retainJSCallback = nil; + } else if (RCTGetTurboModuleCleanupMode() == kRCTGlobalScopeUsingRetainJSCallback) { + // Use LongLivedObjectCollection singleton via the _retainJSCallback + _retainJSCallback = ^(jsi::Function &&callback, jsi::Runtime &runtime, std::shared_ptr jsInvoker2) { + return CallbackWrapper::createWeak(std::move(callback), runtime, jsInvoker2); + }; + } else if (RCTGetTurboModuleCleanupMode() == kRCTTurboModuleManagerScope) { + // Use a LongLivedObjectCollection scoped to the TurboModuleManager + _longLivedObjectCollection = std::make_shared(); + __block std::shared_ptr longLivedObjectCollection = _longLivedObjectCollection; + _retainJSCallback = ^(jsi::Function &&callback, jsi::Runtime &runtime, std::shared_ptr jsInvoker2) { + return CallbackWrapper::createWeak(longLivedObjectCollection, std::move(callback), runtime, jsInvoker2); + }; + } } return self; } @@ -263,6 +284,9 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name Class moduleClass = [module class]; dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey); + if (methodQueue == nil) { + RCTLogError(@"TurboModule \"%@\" was not associated with a method queue.", moduleClass); + } /** * Step 2c: Create and native CallInvoker from the TurboModule's method queue. @@ -277,23 +301,6 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name nativeInvoker = [_bridge decorateNativeCallInvoker:nativeInvoker]; } - ObjCTurboModule::InitParams params = { - .moduleName = moduleName, - .instance = module, - .jsInvoker = _jsInvoker, - .nativeInvoker = nativeInvoker, - .isSyncModule = methodQueue == RCTJSThread, - }; - - // If RCTTurboModule supports creating its own C++ TurboModule object, - // allow it to do so. - if ([module respondsToSelector:@selector(getTurboModule:)]) { - auto turboModule = [module getTurboModule:params]; - assert(turboModule != nullptr); - _turboModuleCache.insert({moduleName, turboModule}); - return turboModule; - } - /** * Step 2d: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that * wraps CxxModule. @@ -306,13 +313,23 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name return turboModule; } + ObjCTurboModule::InitParams params = { + .moduleName = moduleName, + .instance = module, + .jsInvoker = _jsInvoker, + .nativeInvoker = nativeInvoker, + .isSyncModule = methodQueue == RCTJSThread, + .retainJSCallback = _retainJSCallback, + }; + /** * Step 2e: Return an exact sub-class of ObjC TurboModule */ - auto turboModule = [_delegate getTurboModule:moduleName initParams:params]; - if (turboModule != nullptr) { - _turboModuleCache.insert({moduleName, turboModule}); + auto turboModule = [module getTurboModule:params]; + if (turboModule == nullptr) { + RCTLogError(@"TurboModule \"%@\"'s getTurboModule: method returned nil.", moduleClass); } + _turboModuleCache.insert({moduleName, turboModule}); return turboModule; } @@ -353,6 +370,10 @@ - (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName */ - (id)provideRCTTurboModule:(const char *)moduleName { + if (strncmp("RCT", moduleName, 3) == 0) { + moduleName = [[[NSString stringWithUTF8String:moduleName] substringFromIndex:3] UTF8String]; + } + TurboModuleHolder *moduleHolder = [self _getOrCreateTurboModuleHolder:moduleName]; if (!moduleHolder) { @@ -401,9 +422,12 @@ - (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName */ if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) { - std::lock_guard delegateGuard(_turboModuleManagerDelegateMutex); - - moduleClass = [_delegate getModuleClassFromName:moduleName]; + if (RCTTurboModuleManagerDelegateLockingDisabled()) { + moduleClass = [_delegate getModuleClassFromName:moduleName]; + } else { + std::lock_guard delegateGuard(_turboModuleManagerDelegateMutex); + moduleClass = [_delegate getModuleClassFromName:moduleName]; + } } if (!moduleClass) { @@ -485,9 +509,12 @@ - (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName TurboModulePerfLogger::moduleCreateConstructStart(moduleName, moduleId); if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) { - std::lock_guard delegateGuard(_turboModuleManagerDelegateMutex); - - module = [_delegate getModuleInstanceFromClass:moduleClass]; + if (RCTTurboModuleManagerDelegateLockingDisabled()) { + module = [_delegate getModuleInstanceFromClass:moduleClass]; + } else { + std::lock_guard delegateGuard(_turboModuleManagerDelegateMutex); + module = [_delegate getModuleInstanceFromClass:moduleClass]; + } } else { module = [moduleClass new]; } @@ -495,10 +522,6 @@ - (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName TurboModulePerfLogger::moduleCreateSetUpStart(moduleName, moduleId); - if ([module respondsToSelector:@selector(setTurboModuleRegistry:)]) { - [module setTurboModuleRegistry:self]; - } - /** * It is reasonable for NativeModules to not want/need the bridge. * In such cases, they won't have `@synthesize bridge = _bridge` in their @@ -572,13 +595,27 @@ - (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName } @catch (NSException *exception) { RCTLogError( @"%@ has no setter or ivar for its methodQueue, which is not " - "permitted. You must either @synthesize the bridge property, " + "permitted. You must either @synthesize the methodQueue property, " "or provide your own setter method.", RCTBridgeModuleNameForClass([module class])); } } } + /** + * Decorate TurboModules with bridgeless-compatible APIs that call into the bridge. + */ + if (_bridge) { + [_bridge attachBridgeAPIsToTurboModule:module]; + } + + /** + * If the TurboModule conforms to RCTInitializing, invoke its initialize method. + */ + if ([module respondsToSelector:@selector(initialize)]) { + [(id)module initialize]; + } + /** * Attach method queue to id object. * This is necessary because the id object can be eagerly created/initialized before the method @@ -595,7 +632,12 @@ - (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName * rollout. */ if (_bridge) { - RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:(id)module bridge:_bridge]; + RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:(id)module + bridge:_bridge + moduleRegistry:_bridge.moduleRegistry + viewRegistry_DEPRECATED:nil + bundleManager:nil + callableJSModules:nil]; [_bridge registerModuleForFrameUpdates:(id)module withModuleData:data]; } @@ -686,8 +728,7 @@ - (void)installJSBindingWithRuntimeExecutor:(facebook::react::RuntimeExecutor)ru * aren't any strong references to it in ObjC. Hence, we give * __turboModuleProxy a strong reference to TurboModuleManager. */ - auto turboModuleProvider = - [self](const std::string &name, const jsi::Value *schema) -> std::shared_ptr { + auto turboModuleProvider = [self](const std::string &name) -> std::shared_ptr { auto moduleName = name.c_str(); TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName); @@ -717,9 +758,17 @@ - (void)installJSBindingWithRuntimeExecutor:(facebook::react::RuntimeExecutor)ru return turboModule; }; - runtimeExecutor([turboModuleProvider = std::move(turboModuleProvider)](jsi::Runtime &runtime) { - react::TurboModuleBinding::install(runtime, std::move(turboModuleProvider)); - }); + if (RCTGetTurboModuleCleanupMode() == kRCTGlobalScope || + RCTGetTurboModuleCleanupMode() == kRCTGlobalScopeUsingRetainJSCallback) { + runtimeExecutor([turboModuleProvider = std::move(turboModuleProvider)](jsi::Runtime &runtime) { + react::TurboModuleBinding::install(runtime, std::move(turboModuleProvider)); + }); + } else if (RCTGetTurboModuleCleanupMode() == kRCTTurboModuleManagerScope) { + runtimeExecutor([turboModuleProvider = std::move(turboModuleProvider), + longLivedObjectCollection = _longLivedObjectCollection](jsi::Runtime &runtime) { + react::TurboModuleBinding::install(runtime, std::move(turboModuleProvider), longLivedObjectCollection); + }); + } } #pragma mark RCTTurboModuleRegistry @@ -731,6 +780,12 @@ - (id)moduleForName:(const char *)moduleName - (id)moduleForName:(const char *)moduleName warnOnLookupFailure:(BOOL)warnOnLookupFailure { + // When the bridge is invalidating, TurboModules will be nil. + // Therefore, don't (1) do the lookup, and (2) warn on lookup. + if (_invalidating) { + return nil; + } + id module = [self provideRCTTurboModule:moduleName]; if (warnOnLookupFailure && !module) { @@ -778,6 +833,27 @@ - (void)bridgeWillInvalidateModules:(NSNotification *)notification return; } + [self _enterInvalidatingState]; +} + +- (void)bridgeDidInvalidateModules:(NSNotification *)notification +{ + RCTBridge *bridge = notification.userInfo[@"bridge"]; + if (bridge != _bridge) { + return; + } + + [self _invalidateModules]; +} + +- (void)invalidate +{ + [self _enterInvalidatingState]; + [self _invalidateModules]; +} + +- (void)_enterInvalidatingState +{ // This should halt all insertions into _turboModuleHolders if (RCTTurboModuleSharedMutexInitEnabled()) { std::unique_lock guard(_turboModuleHoldersSharedMutex); @@ -788,13 +864,8 @@ - (void)bridgeWillInvalidateModules:(NSNotification *)notification } } -- (void)bridgeDidInvalidateModules:(NSNotification *)notification +- (void)_invalidateModules { - RCTBridge *bridge = notification.userInfo[@"bridge"]; - if (bridge != _bridge) { - return; - } - // Backward-compatibility: RCTInvalidating handling. dispatch_group_t moduleInvalidationGroup = dispatch_group_create(); @@ -812,20 +883,31 @@ - (void)bridgeDidInvalidateModules:(NSNotification *)notification shouldPerfLog:NO]; if ([module respondsToSelector:@selector(invalidate)]) { - if ([module respondsToSelector:@selector(methodQueue)]) { - dispatch_queue_t methodQueue = [module performSelector:@selector(methodQueue)]; - if (methodQueue) { - dispatch_group_enter(moduleInvalidationGroup); - [bridge - dispatchBlock:^{ - [((id)module) invalidate]; - dispatch_group_leave(moduleInvalidationGroup); - } - queue:methodQueue]; - continue; + dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey); + + if (methodQueue == nil) { + RCTLogError( + @"TurboModuleManager: Couldn't invalidate TurboModule \"%@\", because its method queue is nil.", + [module class]); + continue; + } + + dispatch_group_enter(moduleInvalidationGroup); + dispatch_block_t invalidateModule = ^{ + [((id)module) invalidate]; + dispatch_group_leave(moduleInvalidationGroup); + }; + + if (_bridge) { + [_bridge dispatchBlock:invalidateModule queue:methodQueue]; + } else { + // Bridgeless mode + if (methodQueue == RCTJSThread) { + invalidateModule(); + } else { + dispatch_async(methodQueue, invalidateModule); } } - [((id)module) invalidate]; } } @@ -837,26 +919,4 @@ - (void)bridgeDidInvalidateModules:(NSNotification *)notification _turboModuleCache.clear(); } -- (void)invalidate -{ - if (RCTTurboModuleSharedMutexInitEnabled()) { - std::unique_lock guard(_turboModuleHoldersSharedMutex); - _invalidating = true; - } else { - std::lock_guard guard(_turboModuleHoldersMutex); - _invalidating = true; - } - - // Backward-compatibility: RCTInvalidating handling, but not adhering to desired methodQueue. - for (const auto &p : _turboModuleHolders) { - id module = p.second.getModule(); - if ([module respondsToSelector:@selector(invalidate)]) { - [((id)module) invalidate]; - } - } - - _turboModuleHolders.clear(); - _turboModuleCache.clear(); -} - @end diff --git a/android/ReactCommon/react/nativemodule/samples/BUCK b/android/ReactCommon/react/nativemodule/samples/BUCK index b6b0fe3b974e5..d3ef2a8d956dc 100644 --- a/android/ReactCommon/react/nativemodule/samples/BUCK +++ b/android/ReactCommon/react/nativemodule/samples/BUCK @@ -1,5 +1,4 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "OBJC_ARC_PREPROCESSOR_FLAGS", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags") -load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", "react_native_dep", "react_native_target", "react_native_xplat_target", "rn_android_library", "rn_xplat_cxx_library", "subdir_glob") +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", "get_objc_arc_preprocessor_flags", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags", "react_native_dep", "react_native_target", "react_native_xplat_target", "rn_android_library", "rn_xplat_cxx_library", "subdir_glob") rn_xplat_cxx_library( name = "samples", @@ -13,12 +12,6 @@ rn_xplat_cxx_library( ], prefix = "ReactCommon", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), FBJNI_TARGET, @@ -35,18 +28,17 @@ rn_xplat_cxx_library( ], ), fbobjc_compiler_flags = [ - "-Wall", "-fobjc-arc-exceptions", ], fbobjc_inherited_buck_flags = get_static_library_ios_flags(), - fbobjc_preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode(), + fbobjc_preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), force_static = True, ios_deps = [ "//xplat/FBBaseLite:FBBaseLite", - "//xplat/js:RCTLinking", - "//xplat/js:RCTPushNotification", "//xplat/js/react-native-github:RCTCxxBridge", "//xplat/js/react-native-github:RCTCxxModule", + "//xplat/js/react-native-github:RCTLinking", + "//xplat/js/react-native-github:RCTPushNotification", "//xplat/js/react-native-github:ReactInternal", ], ios_exported_headers = subdir_glob( @@ -85,6 +77,7 @@ rn_xplat_cxx_library( rn_android_library( name = "impl", srcs = glob(["platform/android/*.java"]), + autoglob = False, required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/android/ReactCommon/react/nativemodule/samples/platform/android/Android.mk b/android/ReactCommon/react/nativemodule/samples/platform/android/Android.mk index 46dfd7623ae02..a000332e34c3e 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/android/Android.mk +++ b/android/ReactCommon/react/nativemodule/samples/platform/android/Android.mk @@ -10,9 +10,9 @@ LOCAL_MODULE := sampleturbomodule LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_SHARED_LIBRARIES := libfbjni libreact_nativemodule_core +LOCAL_SHARED_LIBRARIES := libfbjni libreact_nativemodule_core libjsi LOCAL_CFLAGS := \ -DLOG_TAG=\"ReactNative\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java b/android/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java index 8475fbc471129..f992e5d6ae1c6 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java +++ b/android/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java @@ -42,6 +42,9 @@ public NativeSampleTurboModuleSpec(ReactApplicationContext reactContext) { @ReactMethod(isBlockingSynchronousMethod = true) public abstract WritableMap getObject(ReadableMap arg); + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract WritableMap getUnsafeObject(ReadableMap arg); + @ReactMethod public abstract void voidFunc(); diff --git a/android/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java b/android/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java index bc357d2baa3d9..f919eb5bd2dd0 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java +++ b/android/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java @@ -109,6 +109,16 @@ public WritableMap getObject(ReadableMap arg) { return map; } + @DoNotStrip + @Override + @SuppressWarnings("unused") + public WritableMap getUnsafeObject(ReadableMap arg) { + WritableNativeMap map = new WritableNativeMap(); + map.merge(arg); + log("getUnsafeObject", arg, map); + return map; + } + @DoNotStrip @SuppressWarnings("unused") @Override diff --git a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h index 4738cff8d10ad..b14289e1cd1ba 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h +++ b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h @@ -26,6 +26,7 @@ - (NSString *)getString:(NSString *)arg; - (NSArray> *)getArray:(NSArray *)arg; - (NSDictionary *)getObject:(NSDictionary *)arg; +- (NSDictionary *)getUnsafeObject:(NSDictionary *)arg; - (NSNumber *)getRootTag:(double)arg; - (NSDictionary *)getValue:(double)x y:(NSString *)y z:(NSDictionary *)z; - (void)getValueWithCallback:(RCTResponseSenderBlock)callback; diff --git a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm index 70f69b107babb..7337f63d51814 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm +++ b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm @@ -70,6 +70,16 @@ .invokeObjCMethod(rt, ObjectKind, "getObject", @selector(getObject:), args, count); } +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getUnsafeObject( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) +{ + return static_cast(turboModule) + .invokeObjCMethod(rt, ObjectKind, "getUnsafeObject", @selector(getUnsafeObject:), args, count); +} + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag( facebook::jsi::Runtime &rt, TurboModule &turboModule, @@ -130,6 +140,7 @@ methodMap_["getString"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; methodMap_["getArray"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; methodMap_["getObject"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; + methodMap_["getUnsafeObject"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getUnsafeObject}; methodMap_["getRootTag"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag}; methodMap_["getValue"] = MethodMetadata{3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; methodMap_["getValueWithCallback"] = diff --git a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm index 29671c92ba802..8306c561744e2 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm +++ b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm @@ -28,6 +28,12 @@ @implementation RCTSampleTurboCxxModule_v1 return nullptr; } +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return nullptr; +} + @end @implementation RCTSampleTurboCxxModule_v2 @@ -39,4 +45,10 @@ @implementation RCTSampleTurboCxxModule_v2 return std::make_unique(); } +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return nullptr; +} + @end diff --git a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm index 526dc433a165f..fc742b492fa6a 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm +++ b/android/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm @@ -17,9 +17,6 @@ @implementation RCTSampleTurboModule // Backward-compatible export RCT_EXPORT_MODULE() -@synthesize bridge = _bridge; -@synthesize turboModuleRegistry = _turboModuleRegistry; - // Backward-compatible queue configuration + (BOOL)requiresMainQueueSetup { @@ -97,6 +94,11 @@ - (NSDictionary *)constantsToExport return arg; } +RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSDictionary *, getUnsafeObject : (NSDictionary *)arg) +{ + return arg; +} + RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSNumber *, getRootTag : (double)arg) { return @(arg); diff --git a/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp b/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp index 5eea42c3d8058..a66b01ef2c16b 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp +++ b/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp @@ -69,6 +69,12 @@ std::vector SampleTurboCxxModuleLegacyImpl::getMethods() { return getObject(xplat::jsArgAsObject(args, 0)); }, CxxModule::SyncTag), + CxxModule::Method( + "getUnsafeObject", + [this](folly::dynamic args) { + return getUnsafeObject(xplat::jsArgAsObject(args, 0)); + }, + CxxModule::SyncTag), CxxModule::Method( "getRootTag", [this](folly::dynamic args) { @@ -126,6 +132,11 @@ folly::dynamic SampleTurboCxxModuleLegacyImpl::getObject( return arg; } +folly::dynamic SampleTurboCxxModuleLegacyImpl::getUnsafeObject( + const folly::dynamic &arg) { + return arg; +} + double SampleTurboCxxModuleLegacyImpl::getRootTag(double arg) { return arg; } diff --git a/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h b/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h index 72d0e3b4adbf4..a3aa44062f572 100644 --- a/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h +++ b/android/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h @@ -31,6 +31,7 @@ class SampleTurboCxxModuleLegacyImpl std::string getString(const std::string &arg); folly::dynamic getArray(const folly::dynamic &arg); folly::dynamic getObject(const folly::dynamic &arg); + folly::dynamic getUnsafeObject(const folly::dynamic &arg); double getRootTag(double arg); folly::dynamic getValue(double x, const std::string &y, const folly::dynamic &z); diff --git a/android/ReactCommon/react/renderer/animations/Android.mk b/android/ReactCommon/react/renderer/animations/Android.mk index 85e6d3f3ce948..c637dbb0d6005 100644 --- a/android/ReactCommon/react/renderer/animations/Android.mk +++ b/android/ReactCommon/react/renderer/animations/Android.mk @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libreact_render_graphics libglog_init glog libruntimeexecutor libyoga libreact_render_components_view libreact_render_uimanager libfolly_futures libreact_render_componentregistry libreactconfig libfolly_json libjsi libreact_render_core libreact_render_debug libreact_render_mounting +LOCAL_SHARED_LIBRARIES := libreact_render_graphics libglog_init glog libruntimeexecutor libyoga librrc_view libreact_render_uimanager libfolly_futures libreact_render_componentregistry libreactconfig libfolly_json libjsi libreact_render_core libreact_render_debug libreact_render_mounting libreact_debug include $(BUILD_SHARED_LIBRARY) @@ -39,3 +39,4 @@ $(call import-module,react/renderer/mounting) $(call import-module,react/renderer/uimanager) $(call import-module,yogajni) $(call import-module,runtimeexecutor) +$(call import-module,react/debug) diff --git a/android/ReactCommon/react/renderer/animations/BUCK b/android/ReactCommon/react/renderer/animations/BUCK index aef6e64865e7b..72a29efdfc0fc 100644 --- a/android/ReactCommon/react/renderer/animations/BUCK +++ b/android/ReactCommon/react/renderer/animations/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -32,10 +32,6 @@ rn_xplat_cxx_library( prefix = "react/renderer/animations", ), compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", "-lm", ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, @@ -59,8 +55,10 @@ rn_xplat_cxx_library( "//xplat/jsi:JSIDynamic", "//xplat/jsi:jsi", react_native_xplat_target("react/config:config"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/components/view:view"), + react_native_xplat_target("react/renderer/components/image:image"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/mounting:mounting"), @@ -76,7 +74,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], @@ -90,6 +88,7 @@ fb_xplat_cxx_test( react_native_xplat_target("react/renderer/components/root:root"), react_native_xplat_target("react/renderer/components/scrollview:scrollview"), react_native_xplat_target("react/renderer/components/view:view"), + react_native_xplat_target("react/test_utils:test_utils"), "//xplat/js/react-native-github:generated_components-rncore", ], ) diff --git a/android/ReactCommon/react/renderer/animations/LayoutAnimationCallbackWrapper.h b/android/ReactCommon/react/renderer/animations/LayoutAnimationCallbackWrapper.h new file mode 100644 index 0000000000000..2b0b6c167b6b0 --- /dev/null +++ b/android/ReactCommon/react/renderer/animations/LayoutAnimationCallbackWrapper.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +class LayoutAnimationCallbackWrapper { + public: + LayoutAnimationCallbackWrapper(jsi::Function &&callback) + : callback_(std::make_shared(std::move(callback))) {} + LayoutAnimationCallbackWrapper() : callback_(nullptr) {} + + void call(jsi::Runtime &runtime) const { + if (callback_) { + callback_->call(runtime); + callback_.reset(); + } + } + + private: + mutable std::shared_ptr callback_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp b/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp index 4c382f3ead5fd..8933bbe2c508f 100644 --- a/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp +++ b/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp @@ -7,118 +7,14 @@ #include "LayoutAnimationDriver.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - #include +#include +#include +#include namespace facebook { namespace react { -static double -getProgressFromValues(double start, double end, double currentValue) { - auto opacityMinmax = std::minmax({start, end}); - auto min = opacityMinmax.first; - auto max = opacityMinmax.second; - return ( - currentValue < min - ? 0 - : (currentValue > max ? 0 : ((max - currentValue) / (max - min)))); -} - -/** - * Given an animation and a ShadowView with properties set on it, detect how - * far through the animation the ShadowView has progressed. - * - * @param mutationsList - * @param now - */ -double LayoutAnimationDriver::getProgressThroughAnimation( - AnimationKeyFrame const &keyFrame, - LayoutAnimation const *layoutAnimation, - ShadowView const &animationStateView) const { - auto layoutAnimationConfig = layoutAnimation->layoutAnimationConfig; - auto const mutationConfig = - *(keyFrame.type == AnimationConfigurationType::Delete - ? layoutAnimationConfig.deleteConfig - : (keyFrame.type == AnimationConfigurationType::Create - ? layoutAnimationConfig.createConfig - : layoutAnimationConfig.updateConfig)); - - auto initialProps = keyFrame.viewStart.props; - auto finalProps = keyFrame.viewEnd.props; - - if (mutationConfig.animationProperty == AnimationProperty::Opacity) { - // Detect progress through opacity animation. - const auto &oldViewProps = - dynamic_cast(initialProps.get()); - const auto &newViewProps = - dynamic_cast(finalProps.get()); - const auto &animationStateViewProps = - dynamic_cast(animationStateView.props.get()); - if (oldViewProps != nullptr && newViewProps != nullptr && - animationStateViewProps != nullptr) { - return getProgressFromValues( - oldViewProps->opacity, - newViewProps->opacity, - animationStateViewProps->opacity); - } - } else if ( - mutationConfig.animationProperty != AnimationProperty::NotApplicable) { - // Detect progress through layout animation. - LayoutMetrics const &finalLayoutMetrics = keyFrame.viewEnd.layoutMetrics; - LayoutMetrics const &baselineLayoutMetrics = - keyFrame.viewStart.layoutMetrics; - LayoutMetrics const &animationStateLayoutMetrics = - animationStateView.layoutMetrics; - - if (baselineLayoutMetrics.frame.size.height != - finalLayoutMetrics.frame.size.height) { - return getProgressFromValues( - baselineLayoutMetrics.frame.size.height, - finalLayoutMetrics.frame.size.height, - animationStateLayoutMetrics.frame.size.height); - } - if (baselineLayoutMetrics.frame.size.width != - finalLayoutMetrics.frame.size.width) { - return getProgressFromValues( - baselineLayoutMetrics.frame.size.width, - finalLayoutMetrics.frame.size.width, - animationStateLayoutMetrics.frame.size.width); - } - if (baselineLayoutMetrics.frame.origin.x != - finalLayoutMetrics.frame.origin.x) { - return getProgressFromValues( - baselineLayoutMetrics.frame.origin.x, - finalLayoutMetrics.frame.origin.x, - animationStateLayoutMetrics.frame.origin.x); - } - if (baselineLayoutMetrics.frame.origin.y != - finalLayoutMetrics.frame.origin.y) { - return getProgressFromValues( - baselineLayoutMetrics.frame.origin.y, - finalLayoutMetrics.frame.origin.y, - animationStateLayoutMetrics.frame.origin.y); - } - } - - return 0; -} - void LayoutAnimationDriver::animationMutationsForFrame( SurfaceId surfaceId, ShadowViewMutation::List &mutationsList, @@ -132,10 +28,7 @@ void LayoutAnimationDriver::animationMutationsForFrame( } int incompleteAnimations = 0; - for (const auto &keyframe : animation.keyFrames) { - if (keyframe.type == AnimationConfigurationType::Noop) { - continue; - } + for (auto &keyframe : animation.keyFrames) { if (keyframe.invalidated) { continue; } @@ -146,7 +39,7 @@ void LayoutAnimationDriver::animationMutationsForFrame( // The contract with the "keyframes generation" phase is that any animated // node will have a valid configuration. auto const layoutAnimationConfig = animation.layoutAnimationConfig; - auto const mutationConfig = + auto const &mutationConfig = (keyframe.type == AnimationConfigurationType::Delete ? layoutAnimationConfig.deleteConfig : (keyframe.type == AnimationConfigurationType::Create @@ -155,7 +48,7 @@ void LayoutAnimationDriver::animationMutationsForFrame( // Interpolate std::pair progress = - calculateAnimationProgress(now, animation, *mutationConfig); + calculateAnimationProgress(now, animation, mutationConfig); double animationTimeProgressLinear = progress.first; double animationInterpolationFactor = progress.second; @@ -163,11 +56,13 @@ void LayoutAnimationDriver::animationMutationsForFrame( animationInterpolationFactor, baselineShadowView, finalShadowView); // Create the mutation instruction - auto updateMutation = ShadowViewMutation::UpdateMutation( - keyframe.parentView, baselineShadowView, mutatedShadowView, -1); - mutationsList.push_back(updateMutation); + mutationsList.emplace_back(ShadowViewMutation::UpdateMutation( + keyframe.viewPrev, mutatedShadowView)); + PrintMutationInstruction("Animation Progress:", updateMutation); + keyframe.viewPrev = std::move(mutatedShadowView); + if (animationTimeProgressLinear < 1) { incompleteAnimations++; } @@ -191,38 +86,11 @@ void LayoutAnimationDriver::animationMutationsForFrame( if (keyframe.invalidated) { continue; } - if (keyframe.finalMutationForKeyFrame.hasValue()) { - auto const &finalMutationForKeyFrame = - *keyframe.finalMutationForKeyFrame; - PrintMutationInstruction( - "Animation Complete: Queuing up Final Mutation:", - finalMutationForKeyFrame); - - // Copy so that if something else mutates the inflight animations, it - // won't change this mutation after this point. - ShadowView oldShadowView{}; - if (finalMutationForKeyFrame.type != - ShadowViewMutation::Type::Update) { - oldShadowView = finalMutationForKeyFrame.oldChildShadowView; - } - mutationsList.push_back( - ShadowViewMutation{finalMutationForKeyFrame.type, - finalMutationForKeyFrame.parentShadowView, - oldShadowView, - finalMutationForKeyFrame.newChildShadowView, - finalMutationForKeyFrame.index}); - } else { - // Issue a final UPDATE so that the final props object sent to the - // mounting layer is the same as the one on the ShadowTree. This is - // mostly to make the MountingCoordinator StubViewTree assertions - // pass. - mutationsList.push_back( - ShadowViewMutation{ShadowViewMutation::Type::Update, - keyframe.parentView, - {}, - keyframe.viewEnd, - -1}); - } + queueFinalMutationsForCompletedKeyFrame( + keyframe, + mutationsList, + false, + "LayoutAnimationDriver: Animation Completed"); } it = inflightAnimations_.erase(it); diff --git a/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.h b/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.h index 59033afca1446..428eb08ecad98 100644 --- a/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.h +++ b/android/ReactCommon/react/renderer/animations/LayoutAnimationDriver.h @@ -7,16 +7,9 @@ #pragma once -#include -#include -#include -#include -#include -#include - -#include - -#include "LayoutAnimationKeyFrameManager.h" +#include +#include +#include namespace facebook { namespace react { @@ -25,20 +18,18 @@ class LayoutAnimationDriver : public LayoutAnimationKeyFrameManager { public: LayoutAnimationDriver( RuntimeExecutor runtimeExecutor, + ContextContainer::Shared &contextContainer, LayoutAnimationStatusDelegate *delegate) - : LayoutAnimationKeyFrameManager(runtimeExecutor, delegate) {} - - virtual ~LayoutAnimationDriver() {} + : LayoutAnimationKeyFrameManager( + runtimeExecutor, + contextContainer, + delegate) {} protected: virtual void animationMutationsForFrame( SurfaceId surfaceId, ShadowViewMutation::List &mutationsList, uint64_t now) const override; - virtual double getProgressThroughAnimation( - AnimationKeyFrame const &keyFrame, - LayoutAnimation const *layoutAnimation, - ShadowView const &animationStateView) const override; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 394a78c5d05ae..13e2f11155fe6 100644 --- a/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -8,22 +8,24 @@ #include "LayoutAnimationKeyFrameManager.h" #include -#include +#include +#include + +#include +#include +#include #include -#include +#include #include #include #include -#include #include +#include #include #include -#include - -#include -#include #include +#include #include @@ -57,9 +59,21 @@ static std::string GetMutationInstructionString( void PrintMutationInstruction( std::string message, ShadowViewMutation const &mutation) { - LOG(ERROR) << message - << " Mutation: " << GetMutationInstructionString(mutation); + [&](std::ostream &stream) -> std::ostream & { + stream << message + << " Mutation: " << GetMutationInstructionString(mutation); + if (mutation.oldChildShadowView.tag != 0) { + stream << " old hash: ##" + << std::hash{}(mutation.oldChildShadowView); + } + if (mutation.newChildShadowView.tag != 0) { + stream << " new hash: ##" + << std::hash{}(mutation.newChildShadowView); + } + return stream; + }(LOG(ERROR)); } + void PrintMutationInstructionRelative( std::string message, ShadowViewMutation const &mutation, @@ -71,198 +85,27 @@ void PrintMutationInstructionRelative( } #endif -static better::optional parseAnimationType(std::string param) { - if (param == "spring") { - return better::optional(AnimationType::Spring); - } - if (param == "linear") { - return better::optional(AnimationType::Linear); - } - if (param == "easeInEaseOut") { - return better::optional(AnimationType::EaseInEaseOut); - } - if (param == "easeIn") { - return better::optional(AnimationType::EaseIn); - } - if (param == "easeOut") { - return better::optional(AnimationType::EaseOut); - } - if (param == "keyboard") { - return better::optional(AnimationType::Keyboard); - } - - LOG(ERROR) << "Error parsing animation type: " << param; - return {}; -} - -static better::optional parseAnimationProperty( - std::string param) { - if (param == "opacity") { - return better::optional(AnimationProperty::Opacity); - } - if (param == "scaleX") { - return better::optional(AnimationProperty::ScaleX); - } - if (param == "scaleY") { - return better::optional(AnimationProperty::ScaleY); - } - if (param == "scaleXY") { - return better::optional(AnimationProperty::ScaleXY); - } - - LOG(ERROR) << "Error parsing animation property: " << param; - return {}; -} - -static better::optional parseAnimationConfig( - folly::dynamic const &config, - double defaultDuration, - bool parsePropertyType) { - if (config.empty() || !config.isObject()) { - return better::optional( - AnimationConfig{AnimationType::Linear, - AnimationProperty::NotApplicable, - defaultDuration, - 0, - 0, - 0}); - } - - auto const typeIt = config.find("type"); - if (typeIt == config.items().end()) { - LOG(ERROR) << "Error parsing animation config: could not find field `type`"; - return {}; - } - auto const animationTypeParam = typeIt->second; - if (animationTypeParam.empty() || !animationTypeParam.isString()) { - LOG(ERROR) - << "Error parsing animation config: could not unwrap field `type`"; - return {}; - } - const auto animationType = parseAnimationType(animationTypeParam.asString()); - if (!animationType) { - LOG(ERROR) - << "Error parsing animation config: could not parse field `type`"; - return {}; - } - - AnimationProperty animationProperty = AnimationProperty::NotApplicable; - if (parsePropertyType) { - auto const propertyIt = config.find("property"); - if (propertyIt == config.items().end()) { - LOG(ERROR) - << "Error parsing animation config: could not find field `property`"; - return {}; - } - auto const animationPropertyParam = propertyIt->second; - if (animationPropertyParam.empty() || !animationPropertyParam.isString()) { - LOG(ERROR) - << "Error parsing animation config: could not unwrap field `property`"; - return {}; - } - const auto animationPropertyParsed = - parseAnimationProperty(animationPropertyParam.asString()); - if (!animationPropertyParsed) { - LOG(ERROR) - << "Error parsing animation config: could not parse field `property`"; - return {}; - } - animationProperty = *animationPropertyParsed; - } - - double duration = defaultDuration; - double delay = 0; - double springDamping = 0.5; - double initialVelocity = 0; - - auto const durationIt = config.find("duration"); - if (durationIt != config.items().end()) { - if (durationIt->second.isDouble()) { - duration = durationIt->second.asDouble(); - } else { - LOG(ERROR) - << "Error parsing animation config: field `duration` must be a number"; - return {}; - } - } - - auto const delayIt = config.find("delay"); - if (delayIt != config.items().end()) { - if (delayIt->second.isDouble()) { - delay = delayIt->second.asDouble(); - } else { - LOG(ERROR) - << "Error parsing animation config: field `delay` must be a number"; - return {}; - } - } - - auto const springDampingIt = config.find("springDamping"); - if (springDampingIt != config.items().end() && - springDampingIt->second.isDouble()) { - if (springDampingIt->second.isDouble()) { - springDamping = springDampingIt->second.asDouble(); - } else { - LOG(ERROR) - << "Error parsing animation config: field `springDamping` must be a number"; - return {}; - } - } - - auto const initialVelocityIt = config.find("initialVelocity"); - if (initialVelocityIt != config.items().end()) { - if (initialVelocityIt->second.isDouble()) { - initialVelocity = initialVelocityIt->second.asDouble(); - } else { - LOG(ERROR) - << "Error parsing animation config: field `initialVelocity` must be a number"; - return {}; - } - } - - return better::optional(AnimationConfig{*animationType, - animationProperty, - duration, - delay, - springDamping, - initialVelocity}); +static inline float +interpolateFloats(float coefficient, float oldValue, float newValue) { + return oldValue + (newValue - oldValue) * coefficient; } -// Parse animation config from JS -static better::optional parseLayoutAnimationConfig( - folly::dynamic const &config) { - if (config.empty() || !config.isObject()) { - return {}; - } - - const auto durationIt = config.find("duration"); - if (durationIt == config.items().end() || !durationIt->second.isDouble()) { - return {}; - } - const double duration = durationIt->second.asDouble(); - - const auto createConfigIt = config.find("create"); - const auto createConfig = createConfigIt == config.items().end() - ? better::optional(AnimationConfig{}) - : parseAnimationConfig(createConfigIt->second, duration, true); - - const auto updateConfigIt = config.find("update"); - const auto updateConfig = updateConfigIt == config.items().end() - ? better::optional(AnimationConfig{}) - : parseAnimationConfig(updateConfigIt->second, duration, false); +#pragma mark - - const auto deleteConfigIt = config.find("delete"); - const auto deleteConfig = deleteConfigIt == config.items().end() - ? better::optional(AnimationConfig{}) - : parseAnimationConfig(deleteConfigIt->second, duration, true); - - if (!createConfig || !updateConfig || !deleteConfig) { - return {}; - } +LayoutAnimationKeyFrameManager::LayoutAnimationKeyFrameManager( + RuntimeExecutor runtimeExecutor, + ContextContainer::Shared &contextContainer, + LayoutAnimationStatusDelegate *delegate) + : runtimeExecutor_(runtimeExecutor), + contextContainer_(contextContainer), + layoutAnimationStatusDelegate_(delegate), + now_([]() { + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + }) {} - return better::optional(LayoutAnimationConfig{ - duration, *createConfig, *updateConfig, *deleteConfig}); -} +#pragma mark UIManagerAnimationDelegate methods /** * Globally configure next LayoutAnimation. @@ -292,14 +135,14 @@ void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation( if (layoutAnimationConfig) { std::lock_guard lock(currentAnimationMutex_); - currentAnimation_ = better::optional{ - LayoutAnimation{-1, - 0, - false, - *layoutAnimationConfig, - successCallback, - failureCallback, - {}}}; + uiManagerDidConfigureNextLayoutAnimation(LayoutAnimation{ + -1, + 0, + false, + *layoutAnimationConfig, + successCallback, + failureCallback, + {}}); } else { LOG(ERROR) << "Parsing LayoutAnimationConfig failed: " << (folly::dynamic)config; @@ -308,617 +151,189 @@ void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation( } } -void LayoutAnimationKeyFrameManager::setLayoutAnimationStatusDelegate( - LayoutAnimationStatusDelegate *delegate) const { - std::lock_guard lock(layoutAnimationStatusDelegateMutex_); - layoutAnimationStatusDelegate_ = delegate; +void LayoutAnimationKeyFrameManager::setComponentDescriptorRegistry( + const SharedComponentDescriptorRegistry &componentDescriptorRegistry) { + componentDescriptorRegistry_ = componentDescriptorRegistry; } -bool LayoutAnimationKeyFrameManager::shouldOverridePullTransaction() const { - return shouldAnimateFrame(); +bool LayoutAnimationKeyFrameManager::shouldAnimateFrame() const { + std::lock_guard lock(currentAnimationMutex_); + return currentAnimation_ || !inflightAnimations_.empty(); } void LayoutAnimationKeyFrameManager::stopSurface(SurfaceId surfaceId) { std::lock_guard lock(surfaceIdsToStopMutex_); - surfaceIdsToStop_.push_back(surfaceId); + surfaceIdsToStop_.insert(surfaceId); } -bool LayoutAnimationKeyFrameManager::shouldAnimateFrame() const { - // There is potentially a race here between getting and setting - // `currentMutation_`. We don't want to lock around this because then we're - // creating contention between pullTransaction and the JS thread. - return currentAnimation_ || !inflightAnimations_.empty(); -} +#pragma mark - MountingOverrideDelegate methods -static inline const float -interpolateFloats(float coefficient, float oldValue, float newValue) { - return oldValue + (newValue - oldValue) * coefficient; +bool LayoutAnimationKeyFrameManager::shouldOverridePullTransaction() const { + return shouldAnimateFrame(); } -std::pair -LayoutAnimationKeyFrameManager::calculateAnimationProgress( - uint64_t now, - const LayoutAnimation &animation, - const AnimationConfig &mutationConfig) const { - if (mutationConfig.animationType == AnimationType::None) { - return {1, 1}; - } - - uint64_t startTime = animation.startTime; - uint64_t delay = mutationConfig.delay; - uint64_t endTime = startTime + delay + mutationConfig.duration; - - static const float PI = 3.14159265358979323846; - - if (now >= endTime) { - return {1, 1}; - } - if (now < startTime + delay) { - return {0, 0}; - } +better::optional +LayoutAnimationKeyFrameManager::pullTransaction( + SurfaceId surfaceId, + MountingTransaction::Number transactionNumber, + TransactionTelemetry const &telemetry, + ShadowViewMutationList mutations) const { + simulateImagePropsMemoryAccess(mutations); + // Current time in milliseconds + uint64_t now = now_(); - double linearTimeProgression = 1 - - (double)(endTime - delay - now) / (double)(endTime - animation.startTime); - - if (mutationConfig.animationType == AnimationType::Linear) { - return {linearTimeProgression, linearTimeProgression}; - } else if (mutationConfig.animationType == AnimationType::EaseIn) { - // This is an accelerator-style interpolator. - // In the future, this parameter (2.0) could be adjusted. This has been the - // default for Classic RN forever. - return {linearTimeProgression, pow(linearTimeProgression, 2.0)}; - } else if (mutationConfig.animationType == AnimationType::EaseOut) { - // This is an decelerator-style interpolator. - // In the future, this parameter (2.0) could be adjusted. This has been the - // default for Classic RN forever. - return {linearTimeProgression, 1.0 - pow(1 - linearTimeProgression, 2.0)}; - } else if (mutationConfig.animationType == AnimationType::EaseInEaseOut) { - // This is a combination of accelerate+decelerate. - // The animation starts and ends slowly, and speeds up in the middle. - return {linearTimeProgression, - cos((linearTimeProgression + 1.0) * PI) / 2 + 0.5}; - } else if (mutationConfig.animationType == AnimationType::Spring) { - // Using mSpringDamping in this equation is not really the exact - // mathematical springDamping, but a good approximation We need to replace - // this equation with the right Factor that accounts for damping and - // friction - double damping = mutationConfig.springDamping; - return { - linearTimeProgression, - (1 + - pow(2, -10 * linearTimeProgression) * - sin((linearTimeProgression - damping / 4) * PI * 2 / damping))}; - } else { - return {linearTimeProgression, linearTimeProgression}; - } -} + bool inflightAnimationsExistInitially = !inflightAnimations_.empty(); + deleteAnimationsForStoppedSurfaces(); -void LayoutAnimationKeyFrameManager:: - adjustImmediateMutationIndicesForDelayedMutations( - SurfaceId surfaceId, - ShadowViewMutation &mutation, - bool skipLastAnimation, - bool lastAnimationOnly) const { - bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; - assert(isRemoveMutation || mutation.type == ShadowViewMutation::Type::Insert); + if (!mutations.empty()) { +#ifdef RN_SHADOW_TREE_INTROSPECTION + { + std::stringstream ss(getDebugDescription(mutations, {})); + std::string to; + while (std::getline(ss, to, '\n')) { + LOG(ERROR) + << "LayoutAnimationKeyFrameManager.cpp: got mutation list: Line: " + << to; + } + }; +#endif - // TODO: turn all of this into a lambda and share code? - if (mutatedViewIsVirtual(mutation)) { - PrintMutationInstruction( - "[IndexAdjustment] Not calling adjustImmediateMutationIndicesForDelayedMutations, is virtual, for:", - mutation); - return; - } + // DEBUG ONLY: list existing inflight animations +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) << "BEGINNING DISPLAYING ONGOING inflightAnimations_!"; + int i = 0; + int j = 0; + for (auto const &inflightAnimation : inflightAnimations_) { + i++; + j = 0; + if (inflightAnimation.completed) { + continue; + } + for (auto &keyframe : inflightAnimation.keyFrames) { + j++; + if (keyframe.invalidated) { + continue; + } + for (const auto &finalMutationForKeyFrame : + keyframe.finalMutationsForKeyFrame) { + if (finalMutationForKeyFrame.mutatedViewIsVirtual()) { + std::string msg = "Animation " + std::to_string(i) + " keyframe " + + std::to_string(j) + ": Final Animation"; + PrintMutationInstruction(msg, finalMutationForKeyFrame); + } else { + LOG(ERROR) << "Animation " << i << " keyframe " << j + << ": on tag: [" << keyframe.viewStart.tag << "]"; + } + } + } + } + LOG(ERROR) << "BEGINNING DONE DISPLAYING ONGOING inflightAnimations_!"; +#endif - PrintMutationInstruction( - "[IndexAdjustment] Calling adjustImmediateMutationIndicesForDelayedMutations for:", - mutation); + PropsParserContext propsParserContext{surfaceId, *contextContainer_}; - // First, collect all final mutations that could impact this immediate - // mutation. - std::vector candidateMutations{}; + // What to do if we detect a conflict? Get current value and make + // that the baseline of the next animation. Scale the remaining time + // in the animation + // Types of conflicts and how we handle them: + // Update -> update: remove the previous update, make it the baseline of the + // next update (with current progress) Update -> remove: same, with final + // mutation being a remove Insert -> update: treat as update->update Insert + // -> remove: same, as update->remove Remove -> update/insert: not possible + // We just collect pairs here of and delete them + // from active animations. If another animation is queued up from the + // current mutations then these deleted mutations will serve as the baseline + // for the next animation. If not, the current mutations are executed + // immediately without issues. + std::vector conflictingAnimations{}; + getAndEraseConflictingAnimations( + surfaceId, mutations, conflictingAnimations); - for (auto inflightAnimationIt = - inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); - inflightAnimationIt != inflightAnimations_.rend(); - inflightAnimationIt++) { - auto &inflightAnimation = *inflightAnimationIt; - if (inflightAnimation.surfaceId != surfaceId) { - continue; - } - if (inflightAnimation.completed) { - continue; + // Are we animating this list of mutations? + better::optional currentAnimation{}; + { + std::lock_guard lock(currentAnimationMutex_); + if (currentAnimation_) { + currentAnimation = std::move(currentAnimation_); + currentAnimation_.reset(); + } } - for (auto it = inflightAnimation.keyFrames.begin(); - it != inflightAnimation.keyFrames.end(); - it++) { - auto &animatedKeyFrame = *it; + if (currentAnimation.hasValue()) { + LayoutAnimation animation = std::move(currentAnimation).value(); + animation.surfaceId = surfaceId; + animation.startTime = now; - if (animatedKeyFrame.invalidated) { - continue; + // Pre-process list to: + // Catch remove+reinsert (reorders) + // Catch delete+create (reparenting) (this should be optimized away at + // the diffing level eventually?) + // TODO: to prevent this step we could tag Remove/Insert mutations as + // being moves on the Differ level, since we know that there? We could use + // TinyMap here, but it's not exposed by Differentiator (yet). + better::set insertedTags; + better::set deletedTags; + better::set reparentedTags; // tags that are deleted and recreated + std::unordered_map movedTags; + for (const auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Insert) { + insertedTags.insert(mutation.newChildShadowView.tag); + } + if (mutation.type == ShadowViewMutation::Type::Delete) { + deletedTags.insert(mutation.oldChildShadowView.tag); + } + if (mutation.type == ShadowViewMutation::Type::Create) { + if (deletedTags.find(mutation.newChildShadowView.tag) != + deletedTags.end()) { + reparentedTags.insert(mutation.newChildShadowView.tag); + } + } } - // Detect if they're in the same view hierarchy, but not equivalent - // (We've already detected direct conflicts and handled them above) - if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) { - continue; - } + // Process mutations list into operations that can be sent to platform + // immediately, and those that need to be animated Deletions, removals, + // updates are delayed and animated. Creations and insertions are sent to + // platform and then "animated in" with opacity updates. Upon completion, + // removals and deletions are sent to platform + ShadowViewMutation::List immediateMutations; - if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { - continue; - } + // Remove operations that are actually moves should be copied to + // "immediate mutations". The corresponding "insert" will also be executed + // immediately and animated as an update. + std::vector keyFramesToAnimate; + auto const layoutAnimationConfig = animation.layoutAnimationConfig; + for (auto const &mutation : mutations) { + ShadowView baselineShadowView = + (mutation.type == ShadowViewMutation::Type::Delete || + mutation.type == ShadowViewMutation::Type::Remove || + mutation.type == ShadowViewMutation::Type::Update + ? mutation.oldChildShadowView + : mutation.newChildShadowView); + react_native_assert(baselineShadowView.tag > 0); + bool haveComponentDescriptor = + hasComponentDescriptorForShadowView(baselineShadowView); - auto &delayedMutation = *animatedKeyFrame.finalMutationForKeyFrame; + // Immediately execute any mutations on a root node + if (baselineShadowView.traits.check( + ShadowNodeTraits::Trait::RootNodeKind)) { + immediateMutations.push_back(mutation); + continue; + } - if (delayedMutation.type != ShadowViewMutation::Type::Remove) { - continue; - } - if (mutatedViewIsVirtual(delayedMutation)) { - continue; - } - if (delayedMutation.oldChildShadowView.tag == - (isRemoveMutation ? mutation.oldChildShadowView.tag - : mutation.newChildShadowView.tag)) { - continue; - } + better::optional executeMutationImmediately{}; - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations CANDIDATE for:", - mutation, - delayedMutation); - candidateMutations.push_back(&delayedMutation); - } + bool isRemoveReinserted = + mutation.type == ShadowViewMutation::Type::Remove && + insertedTags.find(mutation.oldChildShadowView.tag) != + insertedTags.end(); - if (lastAnimationOnly) { - break; - } - } - - // While the mutation keeps being affected, keep checking. We use the vector - // so we only perform one adjustment per delayed mutation. See comments at - // bottom of adjustDelayedMutationIndicesForMutation for further explanation. - bool changed = true; - int adjustedDelta = 0; - while (changed) { - changed = false; - candidateMutations.erase( - std::remove_if( - candidateMutations.begin(), - candidateMutations.end(), - [&](ShadowViewMutation *candidateMutation) { - bool indexConflicts = - (candidateMutation->index < mutation.index || - (isRemoveMutation && - candidateMutation->index == mutation.index)); - if (indexConflicts) { - mutation.index++; - adjustedDelta++; - changed = true; - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting mutation UPWARD", - mutation, - *candidateMutation); - return true; - } - return false; - }), - candidateMutations.end()); - } -} - -void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( - SurfaceId surfaceId, - ShadowViewMutation const &mutation, - bool skipLastAnimation) const { - bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; - bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; - assert(isRemoveMutation || isInsertMutation); - - if (mutatedViewIsVirtual(mutation)) { - PrintMutationInstruction( - "[IndexAdjustment] Not calling adjustDelayedMutationIndicesForMutation, is virtual, for:", - mutation); - return; - } - - // First, collect all final mutations that could impact this immediate - // mutation. - std::vector candidateMutations{}; - - for (auto inflightAnimationIt = - inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); - inflightAnimationIt != inflightAnimations_.rend(); - inflightAnimationIt++) { - auto &inflightAnimation = *inflightAnimationIt; - - if (inflightAnimation.surfaceId != surfaceId) { - continue; - } - if (inflightAnimation.completed) { - continue; - } - - for (auto it = inflightAnimation.keyFrames.begin(); - it != inflightAnimation.keyFrames.end(); - it++) { - auto &animatedKeyFrame = *it; - - if (animatedKeyFrame.invalidated) { - continue; - } - - // Detect if they're in the same view hierarchy, but not equivalent - // (We've already detected direct conflicts and handled them above) - if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) { - continue; - } - - if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { - continue; - } - ShadowViewMutation &finalAnimationMutation = - *animatedKeyFrame.finalMutationForKeyFrame; - - if (finalAnimationMutation.oldChildShadowView.tag == - (isRemoveMutation ? mutation.oldChildShadowView.tag - : mutation.newChildShadowView.tag)) { - continue; - } - - if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) { - continue; - } - if (mutatedViewIsVirtual(*animatedKeyFrame.finalMutationForKeyFrame)) { - continue; - } - - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:", - mutation, - *animatedKeyFrame.finalMutationForKeyFrame); - candidateMutations.push_back( - animatedKeyFrame.finalMutationForKeyFrame.get_pointer()); - } - } - - // Because the finalAnimations are not sorted in any way, it is possible to - // have some sequence like: - // * DELAYED REMOVE 10 from {TAG} - // * DELAYED REMOVE 9 from {TAG} - // * ... - // * DELAYED REMOVE 5 from {TAG} - // with mutation: INSERT 6/REMOVE 6. This would cause the first few mutations - // to *not* be adjusted, even though they would be impacted by mutation or - // vice-versa after later adjustments are applied. Therefore, we just keep - // recursing while there are any changes. This isn't great, but is good enough - // for now until we change these data-structures. - bool changed = true; - while (changed) { - changed = false; - candidateMutations.erase( - std::remove_if( - candidateMutations.begin(), - candidateMutations.end(), - [&mutation, &isRemoveMutation, &isInsertMutation, &changed]( - ShadowViewMutation *candidateMutation) { - if (isRemoveMutation && - mutation.index <= candidateMutation->index) { - candidateMutation->index--; - changed = true; - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: Adjusting mutation DOWNWARD", - mutation, - *candidateMutation); - return true; - } else if ( - isInsertMutation && - mutation.index <= candidateMutation->index) { - candidateMutation->index++; - changed = true; - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: Adjusting mutation UPWARD", - mutation, - *candidateMutation); - return true; - } - return false; - }), - candidateMutations.end()); - } -} - -std::vector> -LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( - SurfaceId surfaceId, - ShadowViewMutationList &mutations, - bool deletesOnly) const { - std::vector> - conflictingAnimations{}; - - for (auto &mutation : mutations) { - if (deletesOnly && mutation.type != ShadowViewMutation::Type::Delete) { - continue; - } - PrintMutationInstruction("getAndEraseConflictingAnimations of: ", mutation); - - auto const &baselineShadowView = - (mutation.type == ShadowViewMutation::Type::Insert || - mutation.type == ShadowViewMutation::Type::Create) - ? mutation.newChildShadowView - : mutation.oldChildShadowView; - - for (auto &inflightAnimation : inflightAnimations_) { - if (inflightAnimation.surfaceId != surfaceId) { - continue; - } - if (inflightAnimation.completed) { - continue; - } - - for (auto it = inflightAnimation.keyFrames.begin(); - it != inflightAnimation.keyFrames.end();) { - auto &animatedKeyFrame = *it; - - if (animatedKeyFrame.invalidated) { - continue; - } - - bool conflicting = animatedKeyFrame.tag == baselineShadowView.tag || - ((mutation.type == ShadowViewMutation::Type::Delete || - mutation.type == ShadowViewMutation::Type::Create) && - animatedKeyFrame.parentView.tag == baselineShadowView.tag) /* || - finalMutationTag == baselineShadowView.tag*/ - ; - - // Conflicting animation detected: if we're mutating a tag under - // animation, or deleting the parent of a tag under animation, or - // reparenting. - if (conflicting) { - auto const layoutAnimationConfig = - inflightAnimation.layoutAnimationConfig; - - auto const mutationConfig = - (animatedKeyFrame.type == AnimationConfigurationType::Delete - ? layoutAnimationConfig.deleteConfig - : (animatedKeyFrame.type == - AnimationConfigurationType::Create - ? layoutAnimationConfig.createConfig - : layoutAnimationConfig.updateConfig)); - - animatedKeyFrame.invalidated = true; - - // We construct a list of all conflicting animations, whether or not - // they have a "final mutation" to execute. This is important with, - // for example, "insert" mutations where the final update needs to set - // opacity to "1", even if there's no final ShadowNode update. - if (!(animatedKeyFrame.finalMutationForKeyFrame.has_value() && - mutatedViewIsVirtual( - *animatedKeyFrame.finalMutationForKeyFrame))) { - conflictingAnimations.push_back(std::make_tuple( - animatedKeyFrame, *mutationConfig, &inflightAnimation)); - } - -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - if (animatedKeyFrame.finalMutationForKeyFrame.has_value()) { - PrintMutationInstructionRelative( - "Found mutation that conflicts with existing in-flight animation:", - mutation, - *animatedKeyFrame.finalMutationForKeyFrame); - } else { - PrintMutationInstruction( - "Found mutation that conflicts with existing in-flight animation (no final mutation):", - mutation); - } -#endif - - // Delete from existing animation - it = inflightAnimation.keyFrames.erase(it); - } else { - it++; - } - } - } - } - - return conflictingAnimations; -} - -better::optional -LayoutAnimationKeyFrameManager::pullTransaction( - SurfaceId surfaceId, - MountingTransaction::Number transactionNumber, - TransactionTelemetry const &telemetry, - ShadowViewMutationList mutations) const { - // Current time in milliseconds - uint64_t now = - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - - bool inflightAnimationsExistInitially = !inflightAnimations_.empty(); - - // Execute stopSurface on any ongoing animations - if (inflightAnimationsExistInitially) { - std::vector surfaceIdsToStop{}; - { - std::lock_guard lock(surfaceIdsToStopMutex_); - surfaceIdsToStop = surfaceIdsToStop_; - surfaceIdsToStop_ = {}; - } - - for (auto it = inflightAnimations_.begin(); - it != inflightAnimations_.end();) { - const auto &animation = *it; - - if (std::find( - surfaceIdsToStop.begin(), - surfaceIdsToStop.end(), - animation.surfaceId) != surfaceIdsToStop.end()) { -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "LayoutAnimations: stopping animation due to stopSurface on " - << surfaceId; -#endif - it = inflightAnimations_.erase(it); - } else { - it++; - } - } - } - - if (!mutations.empty()) { -#ifdef RN_SHADOW_TREE_INTROSPECTION - { - std::stringstream ss(getDebugDescription(mutations, {})); - std::string to; - while (std::getline(ss, to, '\n')) { - LOG(ERROR) - << "LayoutAnimationKeyFrameManager.cpp: got mutation list: Line: " - << to; - } - }; -#endif - - // DEBUG ONLY: list existing inflight animations -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) << "BEGINNING DISPLAYING ONGOING inflightAnimations_!"; - int i = 0; - int j = 0; - for (auto &inflightAnimation : inflightAnimations_) { - i++; - j = 0; - if (inflightAnimation.completed) { - continue; - } - for (auto &keyframe : inflightAnimation.keyFrames) { - j++; - if (keyframe.invalidated) { - continue; - } - if (keyframe.finalMutationForKeyFrame && - !mutatedViewIsVirtual(*keyframe.finalMutationForKeyFrame)) { - std::string msg = "Animation " + std::to_string(i) + " keyframe " + - std::to_string(j) + ": Final Animation"; - PrintMutationInstruction(msg, *keyframe.finalMutationForKeyFrame); - } else { - LOG(ERROR) << "Animation " << i << " keyframe " << j << ": on tag: [" - << keyframe.viewStart.tag << "]"; - } - } - } - LOG(ERROR) << "BEGINNING DONE DISPLAYING ONGOING inflightAnimations_!"; -#endif - - // What to do if we detect a conflict? Get current value and make - // that the baseline of the next animation. Scale the remaining time - // in the animation - // Types of conflicts and how we handle them: - // Update -> update: remove the previous update, make it the baseline of the - // next update (with current progress) Update -> remove: same, with final - // mutation being a remove Insert -> update: treat as update->update Insert - // -> remove: same, as update->remove Remove -> update/insert: not possible - // We just collect pairs here of and delete them - // from active animations. If another animation is queued up from the - // current mutations then these deleted mutations will serve as the baseline - // for the next animation. If not, the current mutations are executed - // immediately without issues. - std::vector< - std::tuple> - conflictingAnimations = - getAndEraseConflictingAnimations(surfaceId, mutations); - - // Are we animating this list of mutations? - better::optional currentAnimation{}; - { - std::lock_guard lock(currentAnimationMutex_); - if (currentAnimation_) { - currentAnimation = std::move(currentAnimation_); - currentAnimation_ = {}; - } - } - - if (currentAnimation) { - LayoutAnimation animation = std::move(currentAnimation.value()); - currentAnimation = {}; - animation.surfaceId = surfaceId; - animation.startTime = now; - - // Pre-process list to: - // Catch remove+reinsert (reorders) - // Catch delete+create (reparenting) (this should be optimized away at - // the diffing level eventually?) - // TODO: to prevent this step we could tag Remove/Insert mutations as - // being moves on the Differ level, since we know that there? We could use - // TinyMap here, but it's not exposed by Differentiator (yet). - std::vector insertedTags; - std::vector deletedTags; - std::vector reparentedTags; // tags that are deleted and recreated - std::unordered_map movedTags; - for (const auto &mutation : mutations) { - if (mutation.type == ShadowViewMutation::Type::Insert) { - insertedTags.push_back(mutation.newChildShadowView.tag); - } - if (mutation.type == ShadowViewMutation::Type::Delete) { - deletedTags.push_back(mutation.oldChildShadowView.tag); - } - if (mutation.type == ShadowViewMutation::Type::Create) { - if (std::find( - deletedTags.begin(), - deletedTags.end(), - mutation.newChildShadowView.tag) != deletedTags.end()) { - reparentedTags.push_back(mutation.newChildShadowView.tag); - } - } - } - - // Process mutations list into operations that can be sent to platform - // immediately, and those that need to be animated Deletions, removals, - // updates are delayed and animated. Creations and insertions are sent to - // platform and then "animated in" with opacity updates. Upon completion, - // removals and deletions are sent to platform - ShadowViewMutation::List immediateMutations; - - // Remove operations that are actually moves should be copied to - // "immediate mutations". The corresponding "insert" will also be executed - // immediately and animated as an update. - std::vector keyFramesToAnimate; - std::vector movesToAnimate; - auto const layoutAnimationConfig = animation.layoutAnimationConfig; - for (auto &mutation : mutations) { - ShadowView baselineShadowView = - (mutation.type == ShadowViewMutation::Type::Delete || - mutation.type == ShadowViewMutation::Type::Remove - ? mutation.oldChildShadowView - : mutation.newChildShadowView); - bool haveComponentDescriptor = - hasComponentDescriptorForShadowView(baselineShadowView); - - bool executeMutationImmediately = false; - - auto mutationConfig = - (mutation.type == ShadowViewMutation::Type::Delete - ? layoutAnimationConfig.deleteConfig - : (mutation.type == ShadowViewMutation::Type::Insert - ? layoutAnimationConfig.createConfig - : layoutAnimationConfig.updateConfig)); - - bool isRemoveReinserted = - mutation.type == ShadowViewMutation::Type::Remove && - std::find( - insertedTags.begin(), - insertedTags.end(), - mutation.oldChildShadowView.tag) != insertedTags.end(); - - // Reparenting can result in a node being removed, inserted (moved) and - // also deleted and created in the same frame, with the same props etc. - // This should eventually be optimized out of the diffing algorithm, but - // for now we detect reparenting and prevent the corresponding - // Delete/Create instructions from being animated. - bool isReparented = std::find( - reparentedTags.begin(), - reparentedTags.end(), - baselineShadowView.tag) != reparentedTags.end(); + // Reparenting can result in a node being removed, inserted (moved) and + // also deleted and created in the same frame, with the same props etc. + // This should eventually be optimized out of the diffing algorithm, but + // for now we detect reparenting and prevent the corresponding + // Delete/Create instructions from being animated. + bool isReparented = + reparentedTags.find(baselineShadowView.tag) != reparentedTags.end(); if (isRemoveReinserted) { movedTags.insert({mutation.oldChildShadowView.tag, mutation}); @@ -927,7 +342,7 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Inserts that follow a "remove" of the same tag should be treated as // an update (move) animation. bool wasInsertedTagRemoved = false; - bool haveConfiguration = mutationConfig.has_value(); + auto movedIt = movedTags.end(); if (mutation.type == ShadowViewMutation::Type::Insert) { // If this is a move, we actually don't want to copy this insert // instruction to animated instructions - we want to @@ -935,31 +350,93 @@ LayoutAnimationKeyFrameManager::pullTransaction( // the layout. // The corresponding Remove and Insert instructions will instead // be treated as "immediate" instructions. - auto movedIt = movedTags.find(mutation.newChildShadowView.tag); + movedIt = movedTags.find(mutation.newChildShadowView.tag); wasInsertedTagRemoved = movedIt != movedTags.end(); - if (wasInsertedTagRemoved) { - mutationConfig = layoutAnimationConfig.updateConfig; - } - haveConfiguration = mutationConfig.has_value(); - - if (wasInsertedTagRemoved && haveConfiguration) { - movesToAnimate.push_back( - AnimationKeyFrame{{}, - AnimationConfigurationType::Update, - mutation.newChildShadowView.tag, - mutation.parentShadowView, - movedIt->second.oldChildShadowView, - mutation.newChildShadowView}); - } } + auto const &mutationConfig = + (mutation.type == ShadowViewMutation::Type::Delete || + (mutation.type == ShadowViewMutation::Type::Remove && + !wasInsertedTagRemoved) + ? layoutAnimationConfig.deleteConfig + : (mutation.type == ShadowViewMutation::Type::Insert && + !wasInsertedTagRemoved + ? layoutAnimationConfig.createConfig + : layoutAnimationConfig.updateConfig)); + bool haveConfiguration = + mutationConfig.animationType != AnimationType::None; + // Creates and inserts should also be executed immediately. // Mutations that would otherwise be animated, but have no // configuration, are also executed immediately. if (isRemoveReinserted || !haveConfiguration || isReparented || mutation.type == ShadowViewMutation::Type::Create || mutation.type == ShadowViewMutation::Type::Insert) { - executeMutationImmediately = true; + executeMutationImmediately = mutation; + + // It is possible, especially in the case of "moves", that we have a + // sequence of operations like: + // UPDATE X + // REMOVE X + // INSERT X + // In these cases, we will have queued up an animation for the UPDATE + // and delayed its execution; the REMOVE and INSERT will be executed + // first; and then the UPDATE will be animating to/from ShadowViews + // that are out-of-sync with what's on the mounting layer. Thus, for + // any UPDATE animations already queued up for this tag, we adjust the + // "previous" ShadowView. + if (mutation.type == ShadowViewMutation::Type::Insert) { + for (auto &keyframe : keyFramesToAnimate) { + if (keyframe.tag == baselineShadowView.tag) { + // If there's already an animation queued up, followed by this + // Insert, it *must* be an Update mutation animation. Other + // sequences should not be possible. + react_native_assert( + keyframe.type == AnimationConfigurationType::Update); + + // The mutation is an "insert", so it must have a + // "newChildShadowView" + react_native_assert(mutation.newChildShadowView.tag > 0); + + // Those asserts don't run in prod. If there's some edge-case + // that we haven't caught yet, we'd crash in debug; make sure we + // don't mutate the prevView in prod. + if (keyframe.type == AnimationConfigurationType::Update && + mutation.newChildShadowView.tag > 0) { + keyframe.viewPrev = mutation.newChildShadowView; + } + } + } + } else if (mutation.type == ShadowViewMutation::Type::Remove) { + for (auto &keyframe : keyFramesToAnimate) { + if (keyframe.tag == baselineShadowView.tag) { + // If there's already an animation queued up, followed by this + // Insert, it *must* be an Update mutation animation. Other + // sequences should not be possible. + react_native_assert( + keyframe.type == AnimationConfigurationType::Update); + + // The mutation is a "remove", so it must have a + // "oldChildShadowView" + react_native_assert(mutation.oldChildShadowView.tag > 0); + + // Those asserts don't run in prod. If there's some edge-case + // that we haven't caught yet, we'd crash in debug; make sure we + // don't mutate the prevView in prod. + // Since normally the UPDATE would have been executed first and + // now it's deferred, we need to change the `oldChildShadowView` + // that is being referenced by the REMOVE mutation. + if (keyframe.type == AnimationConfigurationType::Update && + mutation.oldChildShadowView.tag > 0) { + executeMutationImmediately = + ShadowViewMutation::RemoveMutation( + mutation.parentShadowView, + keyframe.viewPrev, + mutation.index); + } + } + } + } } // Deletes, non-move inserts, updates get animated @@ -970,112 +447,121 @@ LayoutAnimationKeyFrameManager::pullTransaction( mutation.type == ShadowViewMutation::Type::Insert ? mutation.newChildShadowView : mutation.oldChildShadowView); + react_native_assert(viewStart.tag > 0); ShadowView viewFinal = ShadowView( mutation.type == ShadowViewMutation::Type::Update ? mutation.newChildShadowView : viewStart); + react_native_assert(viewFinal.tag > 0); ShadowView parent = mutation.parentShadowView; + react_native_assert( + parent.tag > 0 || + mutation.type == ShadowViewMutation::Type::Update || + mutation.type == ShadowViewMutation::Type::Delete); Tag tag = viewStart.tag; AnimationKeyFrame keyFrame{}; if (mutation.type == ShadowViewMutation::Type::Insert) { - if (mutationConfig->animationProperty == + if (mutationConfig.animationProperty == AnimationProperty::Opacity && haveComponentDescriptor) { auto props = getComponentDescriptorForShadowView(baselineShadowView) - .cloneProps(viewStart.props, {}); - const auto viewProps = - dynamic_cast(props.get()); - if (viewProps != nullptr) { - const_cast(viewProps)->opacity = 0; - } - viewStart.props = props; - } - bool isScaleX = mutationConfig->animationProperty == - AnimationProperty::ScaleX || - mutationConfig->animationProperty == AnimationProperty::ScaleXY; - bool isScaleY = mutationConfig->animationProperty == - AnimationProperty::ScaleY || - mutationConfig->animationProperty == AnimationProperty::ScaleXY; - if ((isScaleX || isScaleY) && haveComponentDescriptor) { - auto props = - getComponentDescriptorForShadowView(baselineShadowView) - .cloneProps(viewStart.props, {}); - const auto viewProps = - dynamic_cast(props.get()); - if (viewProps != nullptr) { - const_cast(viewProps)->transform = - Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1); + .cloneProps(propsParserContext, viewStart.props, {}); + + if (baselineShadowView.traits.check( + ShadowNodeTraits::Trait::ViewKind)) { + auto const &viewProps = + *std::static_pointer_cast(props); + const_cast(viewProps).opacity = 0; } - viewStart.props = props; - } - keyFrame = AnimationKeyFrame{{}, - AnimationConfigurationType::Create, - tag, - parent, - viewStart, - viewFinal, - 0}; - } else if (mutation.type == ShadowViewMutation::Type::Delete) { - if (mutationConfig->animationProperty == - AnimationProperty::Opacity && - haveComponentDescriptor) { - auto props = - getComponentDescriptorForShadowView(baselineShadowView) - .cloneProps(viewFinal.props, {}); - const auto viewProps = - dynamic_cast(props.get()); - if (viewProps != nullptr) { - const_cast(viewProps)->opacity = 0; + react_native_assert(props != nullptr); + if (props != nullptr) { + viewStart.props = props; } - viewFinal.props = props; } - bool isScaleX = mutationConfig->animationProperty == - AnimationProperty::ScaleX || - mutationConfig->animationProperty == AnimationProperty::ScaleXY; - bool isScaleY = mutationConfig->animationProperty == - AnimationProperty::ScaleY || - mutationConfig->animationProperty == AnimationProperty::ScaleXY; + bool isScaleX = + mutationConfig.animationProperty == AnimationProperty::ScaleX || + mutationConfig.animationProperty == AnimationProperty::ScaleXY; + bool isScaleY = + mutationConfig.animationProperty == AnimationProperty::ScaleY || + mutationConfig.animationProperty == AnimationProperty::ScaleXY; if ((isScaleX || isScaleY) && haveComponentDescriptor) { auto props = getComponentDescriptorForShadowView(baselineShadowView) - .cloneProps(viewFinal.props, {}); - const auto viewProps = - dynamic_cast(props.get()); - if (viewProps != nullptr) { - const_cast(viewProps)->transform = + .cloneProps(propsParserContext, viewStart.props, {}); + if (baselineShadowView.traits.check( + ShadowNodeTraits::Trait::ViewKind)) { + auto const &viewProps = + *std::static_pointer_cast(props); + const_cast(viewProps).transform = Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1); } - viewFinal.props = props; + + react_native_assert(props != nullptr); + if (props != nullptr) { + viewStart.props = props; + } } + PrintMutationInstruction( + "Setting up animation KeyFrame for INSERT mutation (Create animation)", + mutation); + keyFrame = AnimationKeyFrame{ - better::optional(mutation), - AnimationConfigurationType::Delete, - tag, - parent, - viewStart, - viewFinal, - 0}; + /* .finalMutationsForKeyFrame = */ {}, + /* .type = */ AnimationConfigurationType::Create, + /* .tag = */ tag, + /* .parentView = */ parent, + /* .viewStart = */ viewStart, + /* .viewEnd = */ viewFinal, + /* .viewPrev = */ baselineShadowView, + /* .initialProgress = */ 0}; + } else if (mutation.type == ShadowViewMutation::Type::Delete) { +// This is just for assertion purposes. +// The NDEBUG check here is to satisfy the compiler in certain environments +// complaining about correspondingRemoveIt being unused. +#ifdef REACT_NATIVE_DEBUG +#ifndef NDEBUG +// This block is temporarily disabled to fix some internal builds. +// In some build configurations, we get a compiler error that +// `correspondingRemoveIt` is unused. +/* Tag deleteTag = mutation.oldChildShadowView.tag; + auto correspondingRemoveIt = std::find_if( + mutations.begin(), + mutations.end(), + [&deleteTag](auto &mutation) { + return mutation.type == ShadowViewMutation::Type::Remove && + mutation.oldChildShadowView.tag == deleteTag; + }); + react_native_assert(correspondingRemoveIt != mutations.end()); +*/ +#endif +#endif + continue; } else if (mutation.type == ShadowViewMutation::Type::Update) { viewFinal = ShadowView(mutation.newChildShadowView); + PrintMutationInstruction( + "Setting up animation KeyFrame for UPDATE mutation (Update animation)", + mutation); + keyFrame = AnimationKeyFrame{ - better::optional(mutation), - AnimationConfigurationType::Update, - tag, - parent, - viewStart, - viewFinal, - 0}; + /* .finalMutationsForKeyFrame = */ {mutation}, + /* .type = */ AnimationConfigurationType::Update, + /* .tag = */ tag, + /* .parentView = */ parent, + /* .viewStart = */ viewStart, + /* .viewEnd = */ viewFinal, + /* .viewPrev = */ baselineShadowView, + /* .initialProgress = */ 0}; } else { // This should just be "Remove" instructions that are not animated // (either this is a "move", or there's a corresponding "Delete" - // that is animated). We configure it as a Noop animation so it is - // executed when all the other animations are completed. - assert(mutation.type == ShadowViewMutation::Type::Remove); + // that is animated). + react_native_assert( + mutation.type == ShadowViewMutation::Type::Remove); Tag removeTag = mutation.oldChildShadowView.tag; auto correspondingInsertIt = std::find_if( @@ -1086,15 +572,78 @@ LayoutAnimationKeyFrameManager::pullTransaction( mutation.newChildShadowView.tag == removeTag; }); if (correspondingInsertIt == mutations.end()) { - PrintMutationInstruction("Queueing Delayed", mutation); + // This is a REMOVE not paired with an INSERT (move), so it must + // be paired with a DELETE. + auto correspondingDeleteIt = std::find_if( + mutations.begin(), + mutations.end(), + [&removeTag](auto &mutation) { + return mutation.type == ShadowViewMutation::Type::Delete && + mutation.oldChildShadowView.tag == removeTag; + }); + react_native_assert(correspondingDeleteIt != mutations.end()); + + auto deleteMutation = *correspondingDeleteIt; + + if (mutationConfig.animationProperty == + AnimationProperty::Opacity && + haveComponentDescriptor) { + auto props = + getComponentDescriptorForShadowView(baselineShadowView) + .cloneProps(propsParserContext, viewFinal.props, {}); + + if (baselineShadowView.traits.check( + ShadowNodeTraits::Trait::ViewKind)) { + auto const &viewProps = + *std::static_pointer_cast(props); + const_cast(viewProps).opacity = 0; + } + + react_native_assert(props != nullptr); + if (props != nullptr) { + viewFinal.props = props; + } + } + bool isScaleX = mutationConfig.animationProperty == + AnimationProperty::ScaleX || + mutationConfig.animationProperty == + AnimationProperty::ScaleXY; + bool isScaleY = mutationConfig.animationProperty == + AnimationProperty::ScaleY || + mutationConfig.animationProperty == + AnimationProperty::ScaleXY; + if ((isScaleX || isScaleY) && haveComponentDescriptor) { + auto props = + getComponentDescriptorForShadowView(baselineShadowView) + .cloneProps(propsParserContext, viewFinal.props, {}); + + if (baselineShadowView.traits.check( + ShadowNodeTraits::Trait::ViewKind)) { + auto const &viewProps = + *std::static_pointer_cast(props); + const_cast(viewProps).transform = + Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1); + } + + react_native_assert(props != nullptr); + if (props != nullptr) { + viewFinal.props = props; + } + } + + PrintMutationInstruction( + "Setting up animation KeyFrame for REMOVE mutation (Delete animation)", + mutation); + keyFrame = AnimationKeyFrame{ - better::optional(mutation), - AnimationConfigurationType::Noop, - tag, - parent, - {}, - {}, - 0}; + /* .finalMutationsForKeyFrame */ {mutation, deleteMutation}, + /* .type */ AnimationConfigurationType::Delete, + /* .tag */ tag, + /* .parentView */ parent, + /* .viewStart */ viewStart, + /* .viewEnd */ viewFinal, + /* .viewPrev */ baselineShadowView, + /* .initialProgress */ 0}; } else { PrintMutationInstruction( "Executing Remove Immediately, due to reordering operation", @@ -1105,37 +654,33 @@ LayoutAnimationKeyFrameManager::pullTransaction( } // Handle conflicting animations - for (auto &conflictingKeyframeTuple : conflictingAnimations) { - auto &conflictingKeyFrame = std::get<0>(conflictingKeyframeTuple); + for (auto &conflictingKeyFrame : conflictingAnimations) { auto const &conflictingMutationBaselineShadowView = conflictingKeyFrame.viewStart; // We've found a conflict. if (conflictingMutationBaselineShadowView.tag == tag) { - // What's the progress of this ongoing animation? - double conflictingAnimationProgress = - calculateAnimationProgress( - now, - *std::get<2>(conflictingKeyframeTuple), - std::get<1>(conflictingKeyframeTuple)) - .first; - - // Get a baseline ShadowView at the current progress of the - // inflight animation. TODO: handle multiple properties being - // animated separately? - auto interpolatedInflightShadowView = - createInterpolatedShadowView( - conflictingAnimationProgress, - conflictingKeyFrame.viewStart, - conflictingKeyFrame.viewEnd); + conflictingKeyFrame.generateFinalSyntheticMutations = false; + // Do NOT update viewStart for a CREATE animation. + if (keyFrame.type == AnimationConfigurationType::Create) { + break; + } + +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "Due to conflict, replacing 'viewStart' of animated keyframe: [" + << conflictingKeyFrame.viewPrev.tag << "] with ##" + << std::hash{}(conflictingKeyFrame.viewPrev); +#endif // Pick a Prop or layout property, depending on the current // animation configuration. Figure out how much progress we've // already made in the current animation, and start the animation // from this point. - keyFrame.viewStart = interpolatedInflightShadowView; - keyFrame.initialProgress = getProgressThroughAnimation( - keyFrame, &animation, interpolatedInflightShadowView); + keyFrame.viewPrev = conflictingKeyFrame.viewPrev; + keyFrame.viewStart = conflictingKeyFrame.viewPrev; + react_native_assert(keyFrame.viewStart.tag > 0); + keyFrame.initialProgress = 0; // We're guaranteed that a tag only has one animation associated // with it, so we can break here. If we support multiple @@ -1145,11 +690,22 @@ LayoutAnimationKeyFrameManager::pullTransaction( } } +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) << "Checking validity of keyframe: [" + << keyFrame.viewStart.tag << "] [" << keyFrame.viewEnd.tag + << "] [" << keyFrame.viewPrev.tag + << "] animation type: " << (int)keyFrame.type; +#endif + react_native_assert(keyFrame.viewStart.tag > 0); + react_native_assert(keyFrame.viewEnd.tag > 0); + react_native_assert(keyFrame.viewPrev.tag > 0); keyFramesToAnimate.push_back(keyFrame); } - if (executeMutationImmediately) { - immediateMutations.push_back(mutation); + if (executeMutationImmediately.hasValue()) { + PrintMutationInstruction( + "Queue Up For Immediate Execution", *executeMutationImmediately); + immediateMutations.push_back(*executeMutationImmediately); } } @@ -1169,11 +725,12 @@ LayoutAnimationKeyFrameManager::pullTransaction( { int idx = 0; for (const auto &keyframe : keyFramesToAnimate) { - if (keyframe.finalMutationForKeyFrame.has_value()) { + for (const auto &finalMutationForKeyFrame : + keyframe.finalMutationsForKeyFrame) { PrintMutationInstruction( std::string("FINAL list: ") + std::to_string(idx) + "/" + std::to_string(keyFramesToAnimate.size()), - *keyframe.finalMutationForKeyFrame); + finalMutationForKeyFrame); } idx++; } @@ -1182,46 +739,64 @@ LayoutAnimationKeyFrameManager::pullTransaction( #endif auto finalConflictingMutations = ShadowViewMutationList{}; - for (auto &conflictingKeyframeTuple : conflictingAnimations) { - auto &keyFrame = std::get<0>(conflictingKeyframeTuple); - if (keyFrame.finalMutationForKeyFrame.hasValue()) { - auto &mutation = *keyFrame.finalMutationForKeyFrame; - if (mutation.type == ShadowViewMutation::Type::Update) { - const auto &mutationInstruction = - ShadowViewMutation::UpdateMutation( - mutation.parentShadowView, - {}, - mutation.newChildShadowView, - mutation.index); - PrintMutationInstruction( - "Queueing up final mutation instruction - update:", - mutationInstruction); - finalConflictingMutations.push_back(std::move(mutationInstruction)); - } else { - PrintMutationInstruction( - "Queueing up final mutation instruction - non-update", - mutation); - finalConflictingMutations.push_back(mutation); + for (auto &keyFrame : conflictingAnimations) { + // Special-case: if we have some (1) ongoing UPDATE animation, + // (2) it conflicted with a new MOVE operation (REMOVE+INSERT) + // without another corresponding UPDATE, we should re-queue the + // keyframe so that its position/props don't suddenly "jump". + if (keyFrame.type == AnimationConfigurationType::Update) { + auto movedIt = movedTags.find(keyFrame.tag); + if (movedIt != movedTags.end()) { + auto newKeyFrameForUpdate = std::find_if( + keyFramesToAnimate.begin(), + keyFramesToAnimate.end(), + [&](auto const &newKeyFrame) { + return newKeyFrame.type == + AnimationConfigurationType::Update && + newKeyFrame.tag == keyFrame.tag; + }); + if (newKeyFrameForUpdate == keyFramesToAnimate.end()) { + keyFrame.invalidated = false; + + // The animation will continue from the current position - we + // restart viewStart to make sure there are no sudden jumps + keyFrame.viewStart = keyFrame.viewPrev; + + // Find the insert mutation that conflicted with this update + for (auto &mutation : immediateMutations) { + if (mutation.newChildShadowView.tag == keyFrame.tag && + (mutation.type == ShadowViewMutation::Insert || + mutation.type == ShadowViewMutation::Create)) { + keyFrame.viewPrev = mutation.newChildShadowView; + keyFrame.viewEnd = mutation.newChildShadowView; + } + } + keyFramesToAnimate.push_back(keyFrame); + continue; + } } - } else { - // If there's no final mutation associated, create a mutation that - // corresponds to the animation being 100% complete. This is important - // for, for example, INSERT mutations being animated from opacity 0 - // to 1. If the animation is interrupted we must force the View to be - // at opacity 1. - auto mutatedShadowView = createInterpolatedShadowView( - 1, keyFrame.viewStart, keyFrame.viewEnd); - auto generatedMutation = ShadowViewMutation::UpdateMutation( - keyFrame.parentView, - keyFrame.viewStart, - std::move(mutatedShadowView), - -1); - PrintMutationInstruction( - "Queueing up final mutation instruction - synthetic", - generatedMutation); - // Create the mutation instruction - finalConflictingMutations.push_back(generatedMutation); } + + // If the "final" mutation is already accounted for, by previously + // setting the correct "viewPrev" of the next conflicting animation, we + // don't want to queue up any final UPDATE mutations here. + bool shouldGenerateSyntheticMutations = + keyFrame.generateFinalSyntheticMutations; + auto numFinalMutations = keyFrame.finalMutationsForKeyFrame.size(); + bool onlyMutationIsUpdate = + (numFinalMutations == 1 && + keyFrame.finalMutationsForKeyFrame[0].type == + ShadowViewMutation::Update); + if (!shouldGenerateSyntheticMutations && + (numFinalMutations == 0 || onlyMutationIsUpdate)) { + continue; + } + + queueFinalMutationsForCompletedKeyFrame( + keyFrame, + finalConflictingMutations, + true, + "KeyFrameManager: Finished Conflicting Keyframe"); } // Make sure that all operations execute in the proper order, since @@ -1243,8 +818,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Knowledge Graph: // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed], // [Delayed] already executed [FrameDelayed] -> assumes - // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> is - // adjusted based on [Delayed], no dependency on [FinalConflicting], + // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> + // is adjusted based on [Delayed], no dependency on [FinalConflicting], // [FrameDelayed] [Delayed] -> assumes [FinalConflicting], // [ImmediateMutations] not executed yet @@ -1252,8 +827,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Knowledge Graph: // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed], // [Delayed] already executed [FrameDelayed] -> assumes - // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> is - // adjusted based on [Delayed], no dependency on [FinalConflicting], + // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> + // is adjusted based on [Delayed], no dependency on [FinalConflicting], // [FrameDelayed] [Delayed] -> adjusted for [FinalConflicting]; assumes // [ImmediateMutations] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING @@ -1271,22 +846,21 @@ LayoutAnimationKeyFrameManager::pullTransaction( // [ImmediateExecutions] -> assumes [FinalConflicting], [Delayed], // [FrameDelayed] already executed [FrameDelayed] -> adjusted for // [Delayed]; assumes [FinalConflicting] already executed - // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on - // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency + // on [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) << "Adjust [FrameDelayed] based on [Delayed]"; #endif for (auto &keyframe : inflightAnimations_.back().keyFrames) { - if (keyframe.finalMutationForKeyFrame.has_value()) { - auto &mutation = *keyframe.finalMutationForKeyFrame; - if (mutation.type == ShadowViewMutation::Type::Insert || - mutation.type == ShadowViewMutation::Type::Remove) { + for (auto &finalMutation : keyframe.finalMutationsForKeyFrame) { + if (finalMutation.type == ShadowViewMutation::Type::Insert || + finalMutation.type == ShadowViewMutation::Type::Remove) { // When adjusting, skip adjusting against last animation - because // all `mutation`s here come from the last animation, so we can't // adjust a batch against itself. adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation, true); + surfaceId, finalMutation, true); } } } @@ -1294,21 +868,21 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Adjust [ImmediateExecutions] based on [Delayed] // Knowledge Graph: // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed]; - // assumes [FinalConflicting] already executed [FrameDelayed] -> adjusted - // for [Delayed]; assumes [FinalConflicting] already executed - // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on - // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // assumes [FinalConflicting] already executed [FrameDelayed] -> + // adjusted for [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency + // on [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet // // THEN, - // Adjust [Delayed] based on [ImmediateExecutions] and [FinalConflicting] - // Knowledge Graph: - // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed]; - // assumes [FinalConflicting] already executed [FrameDelayed] -> adjusted - // for [Delayed]; assumes [FinalConflicting] already executed - // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on - // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for - // [FinalConflicting], [ImmediateExecutions] + // Adjust [Delayed] based on [ImmediateExecutions] and + // [FinalConflicting] Knowledge Graph: [ImmediateExecutions] -> adjusted + // for [FrameDelayed], [Delayed]; assumes [FinalConflicting] already + // executed [FrameDelayed] -> adjusted for [Delayed]; assumes + // [FinalConflicting] already executed [FinalConflicting] -> is adjusted + // based on [Delayed], no dependency on [FinalConflicting], + // [FrameDelayed] [Delayed] -> adjusted for [FinalConflicting], + // [ImmediateExecutions] // // We do these in the same loop because each immediate execution is // impacted by each delayed mutation, and also can impact each delayed @@ -1320,9 +894,9 @@ LayoutAnimationKeyFrameManager::pullTransaction( for (auto &mutation : immediateMutations) { // Note: when adjusting [ImmediateExecutions] based on [FrameDelayed], // we need only adjust Inserts. Since inserts are executed - // highest-index-first, lower indices being delayed does not impact the - // higher-index removals; and conversely, higher indices being delayed - // cannot impact lower index removal, regardless of order. + // highest-index-first, lower indices being delayed does not impact + // the higher-index removals; and conversely, higher indices being + // delayed cannot impact lower index removal, regardless of order. if (mutation.type == ShadowViewMutation::Type::Insert || mutation.type == ShadowViewMutation::Type::Remove) { adjustImmediateMutationIndicesForDelayedMutations( @@ -1336,56 +910,36 @@ LayoutAnimationKeyFrameManager::pullTransaction( } } - // If the knowledge graph progression above is correct, it is now safe to - // execute finalConflictingMutations and immediateMutations in that order, - // and to queue the delayed animations from this frame. + // If the knowledge graph progression above is correct, it is now safe + // to execute finalConflictingMutations and immediateMutations in that + // order, and to queue the delayed animations from this frame. // // Execute the conflicting, delayed operations immediately. Any UPDATE // operations that smoothly transition into another animation will be // overridden by generated UPDATE operations at the end of the list, and // we want any REMOVE or DELETE operations to execute immediately. - // Additionally, this should allow us to avoid performing index adjustment - // between this list of conflicting animations and the batch we're about - // to execute. + // Additionally, this should allow us to avoid performing index + // adjustment between this list of conflicting animations and the batch + // we're about to execute. finalConflictingMutations.insert( finalConflictingMutations.end(), immediateMutations.begin(), immediateMutations.end()); mutations = finalConflictingMutations; - } /* if (currentAnimation) */ else { + } /* if (currentAnimation) */ + else { // If there's no "next" animation, make sure we queue up "final" // operations from all ongoing, conflicting animations. #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) << "No Animation: Queue up final conflicting animations"; #endif ShadowViewMutationList finalMutationsForConflictingAnimations{}; - for (auto &conflictingKeyframeTuple : conflictingAnimations) { - auto &keyFrame = std::get<0>(conflictingKeyframeTuple); - if (keyFrame.finalMutationForKeyFrame.hasValue()) { - PrintMutationInstruction( - "No Animation: Queueing final mutation instruction", - *keyFrame.finalMutationForKeyFrame); - finalMutationsForConflictingAnimations.push_back( - *keyFrame.finalMutationForKeyFrame); - } else { - // If there's no final mutation associated, create a mutation that - // corresponds to the animation being 100% complete. This is important - // for, for example, INSERT mutations being animated from opacity 0 - // to 1. If the animation is interrupted we must force the View to be - // at opacity 1. - auto mutatedShadowView = createInterpolatedShadowView( - 1, keyFrame.viewStart, keyFrame.viewEnd); - auto generatedMutation = ShadowViewMutation::UpdateMutation( - keyFrame.parentView, - keyFrame.viewStart, - std::move(mutatedShadowView), - -1); - PrintMutationInstruction( - "Queueing up final mutation instruction - synthetic", - generatedMutation); - // Create the mutation instruction - finalMutationsForConflictingAnimations.push_back(generatedMutation); - } + for (auto const &keyFrame : conflictingAnimations) { + queueFinalMutationsForCompletedKeyFrame( + keyFrame, + finalMutationsForConflictingAnimations, + true, + "Conflict with non-animated mutation"); } // Make sure that all operations execute in the proper order. @@ -1399,17 +953,17 @@ LayoutAnimationKeyFrameManager::pullTransaction( LOG(ERROR) << "No Animation: Adjust delayed mutations based on all finalMutationsForConflictingAnimations"; #endif - for (auto &mutation : finalMutationsForConflictingAnimations) { + for (auto const &mutation : finalMutationsForConflictingAnimations) { if (mutation.type == ShadowViewMutation::Type::Remove || mutation.type == ShadowViewMutation::Type::Insert) { adjustDelayedMutationIndicesForMutation(surfaceId, mutation); } } - // The ShadowTree layer doesn't realize that certain operations have been - // delayed, so we must adjust all Remove and Insert operations based on - // what else has been deferred, whether we are executing this immediately - // or later. + // The ShadowTree layer doesn't realize that certain operations have + // been delayed, so we must adjust all Remove and Insert operations + // based on what else has been deferred, whether we are executing this + // immediately or later. #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) << "No Animation: Adjust mutations based on remaining delayed mutations / adjust delayed, based on each"; @@ -1442,189 +996,684 @@ LayoutAnimationKeyFrameManager::pullTransaction( ShadowViewMutationList mutationsForAnimation{}; animationMutationsForFrame(surfaceId, mutationsForAnimation, now); - // If any delayed removes were executed, update remaining delayed keyframes -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust all delayed mutations based on final mutations generated by animation driver"; -#endif - for (auto const &mutation : mutationsForAnimation) { - if (mutation.type == ShadowViewMutation::Type::Remove) { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); - } + // If any delayed removes were executed, update remaining delayed keyframes +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "Adjust all delayed mutations based on final mutations generated by animation driver"; +#endif + for (auto const &mutation : mutationsForAnimation) { + if (mutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } + } + + mutations.insert( + mutations.end(), + mutationsForAnimation.begin(), + mutationsForAnimation.end()); + + // DEBUG ONLY: list existing inflight animations +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) << "FINISHING DISPLAYING ONGOING inflightAnimations_!"; + int i = 0; + int j = 0; + for (auto const &inflightAnimation : inflightAnimations_) { + i++; + j = 0; + if (inflightAnimation.completed) { + continue; + } + for (auto &keyframe : inflightAnimation.keyFrames) { + j++; + if (keyframe.invalidated) { + continue; + } + for (auto const &finalMutation : keyframe.finalMutationsForKeyFrame) { + if (!finalMutation.mutatedViewIsVirtual()) { + std::string msg = "Animation " + std::to_string(i) + " keyframe " + + std::to_string(j) + ": Final Animation"; + PrintMutationInstruction(msg, finalMutation); + } + } + } + } + LOG(ERROR) << "FINISHING DONE DISPLAYING ONGOING inflightAnimations_!"; +#endif + + // Signal to delegate if all animations are complete, or if we were not + // animating anything and now some animation exists. + if (inflightAnimationsExistInitially && inflightAnimations_.empty()) { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + if (layoutAnimationStatusDelegate_ != nullptr) { + layoutAnimationStatusDelegate_->onAllAnimationsComplete(); + } + } else if ( + !inflightAnimationsExistInitially && !inflightAnimations_.empty()) { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + if (layoutAnimationStatusDelegate_ != nullptr) { + layoutAnimationStatusDelegate_->onAnimationStarted(); + } + } + + simulateImagePropsMemoryAccess(mutations); + + return MountingTransaction{ + surfaceId, transactionNumber, std::move(mutations), telemetry}; +} + +void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation( + LayoutAnimation layoutAnimation) const { + currentAnimation_ = better::optional{layoutAnimation}; +} + +void LayoutAnimationKeyFrameManager::setLayoutAnimationStatusDelegate( + LayoutAnimationStatusDelegate *delegate) const { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + layoutAnimationStatusDelegate_ = delegate; +} + +void LayoutAnimationKeyFrameManager::setClockNow( + std::function now) { + now_ = now; +} + +void LayoutAnimationKeyFrameManager::enableSkipInvalidatedKeyFrames() { + skipInvalidatedKeyFrames_ = true; +} + +void LayoutAnimationKeyFrameManager::enableCrashOnMissingComponentDescriptor() { + crashOnMissingComponentDescriptor_ = true; +} + +void LayoutAnimationKeyFrameManager::enableSimulateImagePropsMemoryAccess() { + simulateImagePropsMemoryAccess_ = true; +} + +#pragma mark - Protected + +bool LayoutAnimationKeyFrameManager::hasComponentDescriptorForShadowView( + ShadowView const &shadowView) const { + auto hasComponentDescriptor = + componentDescriptorRegistry_->hasComponentDescriptorAt( + shadowView.componentHandle); + + if (crashOnMissingComponentDescriptor_ && !hasComponentDescriptor) { + LOG(FATAL) << "Component descriptor with handle: " + << shadowView.componentHandle + << " doesn't exist. The component name: " + << shadowView.componentName; + } + + return hasComponentDescriptor; +} + +ComponentDescriptor const & +LayoutAnimationKeyFrameManager::getComponentDescriptorForShadowView( + ShadowView const &shadowView) const { + return componentDescriptorRegistry_->at(shadowView.componentHandle); +} + +ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView( + double progress, + ShadowView const &startingView, + ShadowView const &finalView) const { + react_native_assert(startingView.tag > 0); + react_native_assert(finalView.tag > 0); + if (!hasComponentDescriptorForShadowView(startingView)) { + LOG(ERROR) << "No ComponentDescriptor for ShadowView being animated: [" + << startingView.tag << "]"; + react_native_assert(false); + return finalView; + } + + ComponentDescriptor const &componentDescriptor = + getComponentDescriptorForShadowView(startingView); + + // Base the mutated view on the finalView, so that the following stay + // consistent: + // - state + // - eventEmitter + // For now, we do not allow interpolation of state. And we probably never + // will, so make sure we always keep the mounting layer consistent with the + // "final" state. + auto mutatedShadowView = ShadowView(finalView); + react_native_assert(mutatedShadowView.tag > 0); + + react_native_assert(startingView.props != nullptr); + react_native_assert(finalView.props != nullptr); + if (startingView.props == nullptr || finalView.props == nullptr) { + return finalView; + } + + // Animate opacity or scale/transform + PropsParserContext propsParserContext{ + finalView.surfaceId, *contextContainer_}; + mutatedShadowView.props = componentDescriptor.interpolateProps( + propsParserContext, progress, startingView.props, finalView.props); + react_native_assert(mutatedShadowView.props != nullptr); + if (mutatedShadowView.props == nullptr) { + return finalView; + } + + // Interpolate LayoutMetrics + LayoutMetrics const &finalLayoutMetrics = finalView.layoutMetrics; + LayoutMetrics const &baselineLayoutMetrics = startingView.layoutMetrics; + LayoutMetrics interpolatedLayoutMetrics = finalLayoutMetrics; + interpolatedLayoutMetrics.frame.origin.x = interpolateFloats( + progress, + baselineLayoutMetrics.frame.origin.x, + finalLayoutMetrics.frame.origin.x); + interpolatedLayoutMetrics.frame.origin.y = interpolateFloats( + progress, + baselineLayoutMetrics.frame.origin.y, + finalLayoutMetrics.frame.origin.y); + interpolatedLayoutMetrics.frame.size.width = interpolateFloats( + progress, + baselineLayoutMetrics.frame.size.width, + finalLayoutMetrics.frame.size.width); + interpolatedLayoutMetrics.frame.size.height = interpolateFloats( + progress, + baselineLayoutMetrics.frame.size.height, + finalLayoutMetrics.frame.size.height); + mutatedShadowView.layoutMetrics = interpolatedLayoutMetrics; + + return mutatedShadowView; +} + +void LayoutAnimationKeyFrameManager::callCallback( + LayoutAnimationCallbackWrapper const &callback) const { + runtimeExecutor_( + [callback](jsi::Runtime &runtime) { callback.call(runtime); }); +} + +void LayoutAnimationKeyFrameManager::queueFinalMutationsForCompletedKeyFrame( + AnimationKeyFrame const &keyframe, + ShadowViewMutation::List &mutationsList, + bool interrupted, + const std::string &logPrefix) const { + if (skipInvalidatedKeyFrames_ && keyframe.invalidated) { + return; + } + if (keyframe.finalMutationsForKeyFrame.size() > 0) { + // TODO: modularize this segment, it is repeated 2x in KeyFrameManager + // as well. + ShadowView prev = keyframe.viewPrev; + for (auto const &finalMutation : keyframe.finalMutationsForKeyFrame) { + PrintMutationInstruction( + logPrefix + ": Queuing up Final Mutation:", finalMutation); + // Copy so that if something else mutates the inflight animations, + // it won't change this mutation after this point. + switch (finalMutation.type) { + // For CREATE/INSERT this will contain CREATE, INSERT in that order. + // For REMOVE/DELETE, same. + case ShadowViewMutation::Type::Create: + mutationsList.push_back(ShadowViewMutation::CreateMutation( + finalMutation.newChildShadowView)); + break; + case ShadowViewMutation::Type::Delete: + mutationsList.push_back(ShadowViewMutation::DeleteMutation(prev)); + break; + case ShadowViewMutation::Type::Insert: + mutationsList.push_back(ShadowViewMutation::InsertMutation( + finalMutation.parentShadowView, + finalMutation.newChildShadowView, + finalMutation.index)); + break; + case ShadowViewMutation::Type::Remove: + mutationsList.push_back(ShadowViewMutation::RemoveMutation( + finalMutation.parentShadowView, prev, finalMutation.index)); + break; + case ShadowViewMutation::Type::Update: + mutationsList.push_back(ShadowViewMutation::UpdateMutation( + prev, finalMutation.newChildShadowView)); + break; + } + if (finalMutation.newChildShadowView.tag > 0) { + prev = finalMutation.newChildShadowView; + } + } + } else { + // If there's no final mutation associated, create a mutation that + // corresponds to the animation being 100% complete. This is + // important for, for example, INSERT mutations being animated from + // opacity 0 to 1. If the animation is interrupted we must force the + // View to be at opacity 1. For Android - since it passes along only + // deltas, not an entire bag of props - generate an "animation" + // frame corresponding to a final update for this view. Only then, + // generate an update that will cause the ShadowTree to be + // consistent with the Mounting layer by passing viewEnd, + // unmodified, to the mounting layer. This helps with, for example, + // opacity animations. + // This is necessary for INSERT (create) and UPDATE (update) mutations, but + // not REMOVE/DELETE mutations ("delete" animations). + if (interrupted) { + auto mutatedShadowView = + createInterpolatedShadowView(1, keyframe.viewStart, keyframe.viewEnd); + auto generatedPenultimateMutation = ShadowViewMutation::UpdateMutation( + keyframe.viewPrev, mutatedShadowView); + react_native_assert( + generatedPenultimateMutation.oldChildShadowView.tag > 0); + react_native_assert( + generatedPenultimateMutation.newChildShadowView.tag > 0); + PrintMutationInstruction( + "Queueing up penultimate mutation instruction - synthetic", + generatedPenultimateMutation); + mutationsList.push_back(generatedPenultimateMutation); + + auto generatedMutation = ShadowViewMutation::UpdateMutation( + mutatedShadowView, keyframe.viewEnd); + react_native_assert(generatedMutation.oldChildShadowView.tag > 0); + react_native_assert(generatedMutation.newChildShadowView.tag > 0); + PrintMutationInstruction( + "Queueing up final mutation instruction - synthetic", + generatedMutation); + mutationsList.push_back(generatedMutation); + } else { + auto mutation = ShadowViewMutation::UpdateMutation( + keyframe.viewPrev, keyframe.viewEnd); + PrintMutationInstruction( + logPrefix + + "Animation Complete: Queuing up Final Synthetic Mutation:", + mutation); + react_native_assert(mutation.oldChildShadowView.tag > 0); + react_native_assert(mutation.newChildShadowView.tag > 0); + mutationsList.push_back(std::move(mutation)); + } + } +} + +#pragma mark - Private + +void LayoutAnimationKeyFrameManager:: + adjustImmediateMutationIndicesForDelayedMutations( + SurfaceId surfaceId, + ShadowViewMutation &mutation, + bool skipLastAnimation, + bool lastAnimationOnly) const { + bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; + react_native_assert( + isRemoveMutation || mutation.type == ShadowViewMutation::Type::Insert); + + // TODO: turn all of this into a lambda and share code? + if (mutation.mutatedViewIsVirtual()) { + PrintMutationInstruction( + "[IndexAdjustment] Not calling adjustImmediateMutationIndicesForDelayedMutations, is virtual, for:", + mutation); + return; + } + + PrintMutationInstruction( + "[IndexAdjustment] Calling adjustImmediateMutationIndicesForDelayedMutations for:", + mutation); + + // First, collect all final mutations that could impact this immediate + // mutation. + std::vector candidateMutations{}; + + for (auto inflightAnimationIt = + inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); + inflightAnimationIt != inflightAnimations_.rend(); + inflightAnimationIt++) { + auto &inflightAnimation = *inflightAnimationIt; + if (inflightAnimation.surfaceId != surfaceId) { + continue; + } + if (inflightAnimation.completed) { + continue; + } + + for (auto it = inflightAnimation.keyFrames.begin(); + it != inflightAnimation.keyFrames.end(); + it++) { + auto &animatedKeyFrame = *it; + + if (animatedKeyFrame.invalidated) { + continue; + } + + // Detect if they're in the same view hierarchy, but not equivalent + // We've already detected direct conflicts and removed them. + if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) { + continue; + } + + for (auto &delayedMutation : animatedKeyFrame.finalMutationsForKeyFrame) { + if (delayedMutation.type != ShadowViewMutation::Type::Remove) { + continue; + } + if (delayedMutation.mutatedViewIsVirtual()) { + continue; + } + if (delayedMutation.oldChildShadowView.tag == + (isRemoveMutation ? mutation.oldChildShadowView.tag + : mutation.newChildShadowView.tag)) { + continue; + } + + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations CANDIDATE for:", + mutation, + delayedMutation); + candidateMutations.push_back(&delayedMutation); + } + } + + if (lastAnimationOnly) { + break; + } + } + + // While the mutation keeps being affected, keep checking. We use the vector + // so we only perform one adjustment per delayed mutation. See comments at + // bottom of adjustDelayedMutationIndicesForMutation for further explanation. + bool changed = true; + int adjustedDelta = 0; + while (changed) { + changed = false; + candidateMutations.erase( + std::remove_if( + candidateMutations.begin(), + candidateMutations.end(), + [&changed, &mutation, &adjustedDelta, &isRemoveMutation]( + ShadowViewMutation *candidateMutation) { + bool indexConflicts = + (candidateMutation->index < mutation.index || + (isRemoveMutation && + candidateMutation->index == mutation.index)); + if (indexConflicts) { + mutation.index++; + adjustedDelta++; + changed = true; + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting mutation UPWARD", + mutation, + *candidateMutation); + return true; + } + return false; + }), + candidateMutations.end()); + } +} + +void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( + SurfaceId surfaceId, + ShadowViewMutation const &mutation, + bool skipLastAnimation) const { + bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; + bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; + auto tag = isRemoveMutation ? mutation.oldChildShadowView.tag + : mutation.newChildShadowView.tag; + react_native_assert(isRemoveMutation || isInsertMutation); + + if (mutation.mutatedViewIsVirtual()) { + PrintMutationInstruction( + "[IndexAdjustment] Not calling adjustDelayedMutationIndicesForMutation, is virtual, for:", + mutation); + return; } - mutations.insert( - mutations.end(), - mutationsForAnimation.begin(), - mutationsForAnimation.end()); + // First, collect all final mutations that could impact this immediate + // mutation. + std::vector candidateMutations{}; - // DEBUG ONLY: list existing inflight animations -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) << "FINISHING DISPLAYING ONGOING inflightAnimations_!"; - int i = 0; - int j = 0; - for (auto &inflightAnimation : inflightAnimations_) { - i++; - j = 0; + for (auto inflightAnimationIt = + inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); + inflightAnimationIt != inflightAnimations_.rend(); + inflightAnimationIt++) { + auto &inflightAnimation = *inflightAnimationIt; + + if (inflightAnimation.surfaceId != surfaceId) { + continue; + } if (inflightAnimation.completed) { continue; } - for (auto &keyframe : inflightAnimation.keyFrames) { - j++; - if (keyframe.invalidated) { + + for (auto it = inflightAnimation.keyFrames.begin(); + it != inflightAnimation.keyFrames.end(); + it++) { + auto &animatedKeyFrame = *it; + + if (animatedKeyFrame.invalidated) { continue; } - if (keyframe.finalMutationForKeyFrame && - !mutatedViewIsVirtual(*keyframe.finalMutationForKeyFrame)) { - std::string msg = "Animation " + std::to_string(i) + " keyframe " + - std::to_string(j) + ": Final Animation"; - PrintMutationInstruction(msg, *keyframe.finalMutationForKeyFrame); - } else { - LOG(ERROR) << "Animation " << i << " keyframe " << j << ": on tag: [" - << keyframe.viewStart.tag << "]"; + + // Detect if they're in the same view hierarchy, but not equivalent + // (We've already detected direct conflicts and handled them above) + if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) { + continue; } - } - } - LOG(ERROR) << "FINISHING DONE DISPLAYING ONGOING inflightAnimations_!"; -#endif - // Signal to delegate if all animations are complete, or if we were not - // animating anything and now some animation exists. - if (inflightAnimationsExistInitially && inflightAnimations_.empty()) { - std::lock_guard lock(layoutAnimationStatusDelegateMutex_); - if (layoutAnimationStatusDelegate_ != nullptr) { - layoutAnimationStatusDelegate_->onAllAnimationsComplete(); - } - } else if ( - !inflightAnimationsExistInitially && !inflightAnimations_.empty()) { - std::lock_guard lock(layoutAnimationStatusDelegateMutex_); - if (layoutAnimationStatusDelegate_ != nullptr) { - layoutAnimationStatusDelegate_->onAnimationStarted(); + for (auto &finalAnimationMutation : + animatedKeyFrame.finalMutationsForKeyFrame) { + if (finalAnimationMutation.oldChildShadowView.tag == tag) { + continue; + } + + if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) { + continue; + } + if (finalAnimationMutation.mutatedViewIsVirtual()) { + continue; + } + + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:", + mutation, + finalAnimationMutation); + candidateMutations.push_back(&finalAnimationMutation); + } } } - return MountingTransaction{ - surfaceId, transactionNumber, std::move(mutations), telemetry}; + // Because the finalAnimations are not sorted in any way, it is possible to + // have some sequence like: + // * DELAYED REMOVE 10 from {TAG} + // * DELAYED REMOVE 9 from {TAG} + // * ... + // * DELAYED REMOVE 5 from {TAG} + // with mutation: INSERT 6/REMOVE 6. This would cause the first few mutations + // to *not* be adjusted, even though they would be impacted by mutation or + // vice-versa after later adjustments are applied. Therefore, we just keep + // recursing while there are any changes. This isn't great, but is good enough + // for now until we change these data-structures. + bool changed = true; + while (changed) { + changed = false; + candidateMutations.erase( + std::remove_if( + candidateMutations.begin(), + candidateMutations.end(), + [&mutation, &isRemoveMutation, &isInsertMutation, &changed]( + ShadowViewMutation *candidateMutation) { + if (isRemoveMutation && + mutation.index <= candidateMutation->index) { + candidateMutation->index--; + changed = true; + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: Adjusting mutation DOWNWARD", + mutation, + *candidateMutation); + return true; + } else if ( + isInsertMutation && + mutation.index <= candidateMutation->index) { + candidateMutation->index++; + changed = true; + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: Adjusting mutation UPWARD", + mutation, + *candidateMutation); + return true; + } + return false; + }), + candidateMutations.end()); + } } -bool LayoutAnimationKeyFrameManager::mutatedViewIsVirtual( - ShadowViewMutation const &mutation) const { - bool viewIsVirtual = false; - - // TODO: extract this into an Android platform-specific class? - // Explanation: for "Insert" mutations, oldChildShadowView is always empty. - // for "Remove" mutations, newChildShadowView is always empty. -#ifdef ANDROID - viewIsVirtual = - mutation.newChildShadowView.layoutMetrics == EmptyLayoutMetrics && - mutation.oldChildShadowView.layoutMetrics == EmptyLayoutMetrics; -#endif - return viewIsVirtual; -} +void LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( + SurfaceId surfaceId, + ShadowViewMutationList const &mutations, + std::vector &conflictingAnimations) const { + ShadowViewMutationList localConflictingMutations{}; + for (auto const &mutation : mutations) { + bool mutationIsCreateOrDelete = + mutation.type == ShadowViewMutation::Type::Create || + mutation.type == ShadowViewMutation::Type::Delete; + auto const &baselineShadowView = + (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Create) + ? mutation.newChildShadowView + : mutation.oldChildShadowView; + auto baselineTag = baselineShadowView.tag; -bool LayoutAnimationKeyFrameManager::hasComponentDescriptorForShadowView( - ShadowView const &shadowView) const { - return componentDescriptorRegistry_->hasComponentDescriptorAt( - shadowView.componentHandle); -} + for (auto &inflightAnimation : inflightAnimations_) { + if (inflightAnimation.surfaceId != surfaceId) { + continue; + } + if (inflightAnimation.completed) { + continue; + } -ComponentDescriptor const & -LayoutAnimationKeyFrameManager::getComponentDescriptorForShadowView( - ShadowView const &shadowView) const { - return componentDescriptorRegistry_->at(shadowView.componentHandle); -} + for (auto it = inflightAnimation.keyFrames.begin(); + it != inflightAnimation.keyFrames.end();) { + auto &animatedKeyFrame = *it; -void LayoutAnimationKeyFrameManager::setComponentDescriptorRegistry( - const SharedComponentDescriptorRegistry &componentDescriptorRegistry) { - componentDescriptorRegistry_ = componentDescriptorRegistry; -} + if (animatedKeyFrame.invalidated) { + continue; + } -/** - * Given a `progress` between 0 and 1, a mutation and LayoutAnimation config, - * return a ShadowView with mutated props and/or LayoutMetrics. - * - * @param progress - * @param layoutAnimation - * @param animatedMutation - * @return - */ -ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView( - double progress, - ShadowView startingView, - ShadowView finalView) const { - if (!hasComponentDescriptorForShadowView(startingView)) { - return finalView; - } - ComponentDescriptor const &componentDescriptor = - getComponentDescriptorForShadowView(startingView); - auto mutatedShadowView = ShadowView(startingView); + // A conflict is when either: the animated node itself is mutated + // directly; or, the parent of the node is created or deleted. In cases + // of reparenting - say, the parent is deleted but the node was moved to + // a different parent first - the reparenting (remove/insert) conflict + // will be detected before we process the parent DELETE. + // Parent deletion is important because deleting a parent recursively + // deletes all children. If we previously deferred deletion of a child, + // we need to force deletion/removal to happen immediately. + bool conflicting = animatedKeyFrame.tag == baselineTag || + (mutationIsCreateOrDelete && + animatedKeyFrame.parentView.tag == baselineTag && + animatedKeyFrame.parentView.tag != 0); - if (startingView.props == nullptr || finalView.props == nullptr) { - return finalView; - } + // Conflicting animation detected: if we're mutating a tag under + // animation, or deleting the parent of a tag under animation, or + // reparenting. + if (conflicting) { + animatedKeyFrame.invalidated = true; - // Animate opacity or scale/transform - mutatedShadowView.props = componentDescriptor.interpolateProps( - progress, startingView.props, finalView.props); + // We construct a list of all conflicting animations, whether or not + // they have a "final mutation" to execute. This is important with, + // for example, "insert" mutations where the final update needs to set + // opacity to "1", even if there's no final ShadowNode update. + // TODO: don't animate virtual views in the first place? + bool isVirtual = false; + for (const auto &finalMutationForKeyFrame : + animatedKeyFrame.finalMutationsForKeyFrame) { + isVirtual = + isVirtual || finalMutationForKeyFrame.mutatedViewIsVirtual(); - // Interpolate LayoutMetrics - LayoutMetrics const &finalLayoutMetrics = finalView.layoutMetrics; - LayoutMetrics const &baselineLayoutMetrics = startingView.layoutMetrics; - LayoutMetrics interpolatedLayoutMetrics = finalLayoutMetrics; - interpolatedLayoutMetrics.frame.origin.x = interpolateFloats( - progress, - baselineLayoutMetrics.frame.origin.x, - finalLayoutMetrics.frame.origin.x); - interpolatedLayoutMetrics.frame.origin.y = interpolateFloats( - progress, - baselineLayoutMetrics.frame.origin.y, - finalLayoutMetrics.frame.origin.y); - interpolatedLayoutMetrics.frame.size.width = interpolateFloats( - progress, - baselineLayoutMetrics.frame.size.width, - finalLayoutMetrics.frame.size.width); - interpolatedLayoutMetrics.frame.size.height = interpolateFloats( - progress, - baselineLayoutMetrics.frame.size.height, - finalLayoutMetrics.frame.size.height); - mutatedShadowView.layoutMetrics = interpolatedLayoutMetrics; +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + PrintMutationInstructionRelative( + "Found mutation that conflicts with existing in-flight animation:", + mutation, + finalMutationForKeyFrame); +#endif + } - return mutatedShadowView; -} + conflictingAnimations.push_back(animatedKeyFrame); + for (const auto &finalMutationForKeyFrame : + animatedKeyFrame.finalMutationsForKeyFrame) { + if (!isVirtual || + finalMutationForKeyFrame.type == + ShadowViewMutation::Type::Delete) { + localConflictingMutations.push_back(finalMutationForKeyFrame); + } + } -void LayoutAnimationKeyFrameManager::callCallback( - const LayoutAnimationCallbackWrapper &callback) const { - if (callback.readyForCleanup()) { - return; + // Delete from existing animation + it = inflightAnimation.keyFrames.erase(it); + } else { + it++; + } + } + } + } + + // Recurse, in case conflicting mutations conflict with other existing + // animations + if (!localConflictingMutations.empty()) { + getAndEraseConflictingAnimations( + surfaceId, localConflictingMutations, conflictingAnimations); } +} - // Callbacks can only be called once. Replace the callsite with an empty - // CallbackWrapper. We use a unique_ptr to avoid copying into the vector. - std::unique_ptr copiedCallback( - std::make_unique(callback)); +void LayoutAnimationKeyFrameManager::deleteAnimationsForStoppedSurfaces() + const { + bool inflightAnimationsExistInitially = !inflightAnimations_.empty(); - // Call the callback that is being retained in the vector - copiedCallback->call(runtimeExecutor_); + // Execute stopSurface on any ongoing animations + if (inflightAnimationsExistInitially) { + better::set surfaceIdsToStop{}; + { + std::lock_guard lock(surfaceIdsToStopMutex_); + surfaceIdsToStop = surfaceIdsToStop_; + surfaceIdsToStop_.clear(); + } - // Protect with a mutex: this can be called on failure callbacks in the JS - // thread and success callbacks on the UI thread - { - std::lock_guard lock(callbackWrappersPendingMutex_); + for (auto it = inflightAnimations_.begin(); + it != inflightAnimations_.end();) { + const auto &animation = *it; - // Clean any stale data in the retention vector - callbackWrappersPending_.erase( - std::remove_if( - callbackWrappersPending_.begin(), - callbackWrappersPending_.end(), - [](const std::unique_ptr &wrapper) { - return wrapper->readyForCleanup(); - }), - callbackWrappersPending_.end()); +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "LayoutAnimations: stopping animation due to stopSurface on " + << surfaceId; +#endif + if (surfaceIdsToStop.find(animation.surfaceId) != + surfaceIdsToStop.end()) { + it = inflightAnimations_.erase(it); + } else { + it++; + } + } + } +} - // Hold onto a reference to the callback, only while - // LayoutAnimationKeyFrameManager is alive and the callback hasn't completed - // yet. - callbackWrappersPending_.push_back(std::move(copiedCallback)); +void LayoutAnimationKeyFrameManager::simulateImagePropsMemoryAccess( + ShadowViewMutationList const &mutations) const { + if (!simulateImagePropsMemoryAccess_) { + return; + } + for (auto const &mutation : mutations) { + if (mutation.type != ShadowViewMutation::Type::Insert) { + continue; + } + if (strcmp(mutation.newChildShadowView.componentName, "Image") == 0) { + auto const &imageProps = *std::static_pointer_cast( + mutation.newChildShadowView.props); + int temp = 0; + switch (imageProps.resizeMode) { + case ImageResizeMode::Cover: + temp = 1; + break; + case ImageResizeMode::Contain: + temp = 2; + break; + case ImageResizeMode::Stretch: + temp = 3; + break; + case ImageResizeMode::Center: + temp = 4; + break; + case ImageResizeMode::Repeat: + temp = 5; + break; + } + (void)temp; + } } } diff --git a/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h b/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h index 9015aa1b61a95..528fe052dad19 100644 --- a/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h +++ b/android/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h @@ -7,18 +7,13 @@ #pragma once -// Enable some or all of these to enable very verbose logging for -// LayoutAnimations -//#define LAYOUT_ANIMATION_VERBOSE_LOGGING 1 -//#define RN_SHADOW_TREE_INTROSPECTION -//#define RN_DEBUG_STRING_CONVERTIBLE 1 - #include #include -#include +#include +#include +#include #include -#include -#include +#include #include #include #include @@ -41,151 +36,34 @@ void PrintMutationInstructionRelative( #define PrintMutationInstructionRelative(a, b, c) #endif -// This corresponds exactly with JS. -enum class AnimationType { - None, - Spring, - Linear, - EaseInEaseOut, - EaseIn, - EaseOut, - Keyboard -}; -enum class AnimationProperty { - NotApplicable, - Opacity, - ScaleX, - ScaleY, - ScaleXY -}; -enum class AnimationConfigurationType { - Noop, // for animation placeholders that are not animated, and should be - // executed once other animations have completed - Create, - Update, - Delete -}; - -// This corresponds exactly with JS. -struct AnimationConfig { - AnimationType animationType = AnimationType::None; - AnimationProperty animationProperty = AnimationProperty::NotApplicable; - double duration = - 0; // these are perhaps better represented as uint64_t, but they - // come from JS as doubles - double delay = 0; - double springDamping = 0; - double initialVelocity = 0; -}; - -// This corresponds exactly with JS. -struct LayoutAnimationConfig { - double duration; // ms - better::optional createConfig; - better::optional updateConfig; - better::optional deleteConfig; -}; - -struct AnimationKeyFrame { - // The mutation that should be executed once the animation completes - // (optional). - better::optional finalMutationForKeyFrame; - - // The type of animation this is (for configuration purposes) - AnimationConfigurationType type; - - // Tag representing the node being animated. - Tag tag; - - ShadowView parentView; - - // ShadowView representing the start and end points of this animation. - ShadowView viewStart; - ShadowView viewEnd; - - // If an animation interrupts an existing one, the starting state may actually - // be halfway through the intended transition. - double initialProgress; - - bool invalidated{false}; -}; - -class LayoutAnimationCallbackWrapper { - public: - LayoutAnimationCallbackWrapper(jsi::Function &&callback) - : callback_(std::make_shared(std::move(callback))) {} - LayoutAnimationCallbackWrapper() : callback_(nullptr) {} - ~LayoutAnimationCallbackWrapper() {} - - // Copy and assignment-copy constructors should copy callback_, and not - // std::move it. Copying is desirable, otherwise the shared_ptr and - // jsi::Function will be deallocated too early. - - bool readyForCleanup() const { - return callback_ == nullptr || *callComplete_; - } - - void call(const RuntimeExecutor &runtimeExecutor) const { - if (readyForCleanup()) { - return; - } - - std::weak_ptr callable = callback_; - std::shared_ptr callComplete = callComplete_; - - runtimeExecutor( - [=, callComplete = std::move(callComplete)](jsi::Runtime &runtime) { - auto fn = callable.lock(); - - if (!fn || *callComplete) { - return; - } - - fn->call(runtime); - *callComplete = true; - }); - } - - private: - std::shared_ptr callComplete_ = std::make_shared(false); - std::shared_ptr callback_; -}; - -struct LayoutAnimation { - SurfaceId surfaceId; - uint64_t startTime; - bool completed = false; - LayoutAnimationConfig layoutAnimationConfig; - LayoutAnimationCallbackWrapper successCallback; - LayoutAnimationCallbackWrapper failureCallback; - std::vector keyFrames; -}; - class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, public MountingOverrideDelegate { public: LayoutAnimationKeyFrameManager( RuntimeExecutor runtimeExecutor, - LayoutAnimationStatusDelegate *delegate) - : runtimeExecutor_(runtimeExecutor), - layoutAnimationStatusDelegate_(delegate) {} - ~LayoutAnimationKeyFrameManager() {} + ContextContainer::Shared &contextContainer, + LayoutAnimationStatusDelegate *delegate); + +#pragma mark - UIManagerAnimationDelegate methods void uiManagerDidConfigureNextLayoutAnimation( jsi::Runtime &runtime, RawValue const &config, const jsi::Value &successCallbackValue, const jsi::Value &failureCallbackValue) const override; + void setComponentDescriptorRegistry(SharedComponentDescriptorRegistry const & componentDescriptorRegistry) override; // TODO: add SurfaceId to this API as well bool shouldAnimateFrame() const override; - bool shouldOverridePullTransaction() const override; - void stopSurface(SurfaceId surfaceId) override; +#pragma mark - MountingOverrideDelegate methods + + bool shouldOverridePullTransaction() const override; + // This is used to "hijack" the diffing process to figure out which mutations // should be animated. The mutations returned by this function will be // executed immediately. @@ -195,55 +73,56 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, TransactionTelemetry const &telemetry, ShadowViewMutationList mutations) const override; + // Exposed for testing. + void uiManagerDidConfigureNextLayoutAnimation( + LayoutAnimation layoutAnimation) const; + // LayoutAnimationStatusDelegate - this is for the platform to get // signal when animations start and complete. Setting and resetting this // delegate is protected by a mutex; ALL method calls into this delegate are // also protected by the mutex! The only way to set this without a mutex is // via a constructor. - public: void setLayoutAnimationStatusDelegate( LayoutAnimationStatusDelegate *delegate) const; - private: - RuntimeExecutor runtimeExecutor_; - mutable std::mutex layoutAnimationStatusDelegateMutex_; - mutable LayoutAnimationStatusDelegate *layoutAnimationStatusDelegate_{}; + void setClockNow(std::function now); - void adjustImmediateMutationIndicesForDelayedMutations( - SurfaceId surfaceId, - ShadowViewMutation &mutation, - bool skipLastAnimation = false, - bool lastAnimationOnly = false) const; - - void adjustDelayedMutationIndicesForMutation( - SurfaceId surfaceId, - ShadowViewMutation const &mutation, - bool skipLastAnimation = false) const; + void enableSkipInvalidatedKeyFrames(); - std::vector> - getAndEraseConflictingAnimations( - SurfaceId surfaceId, - ShadowViewMutationList &mutations, - bool deletesOnly = false) const; + void enableCrashOnMissingComponentDescriptor(); - mutable std::mutex surfaceIdsToStopMutex_; - mutable std::vector surfaceIdsToStop_{}; + void enableSimulateImagePropsMemoryAccess(); protected: - bool mutatedViewIsVirtual(ShadowViewMutation const &mutation) const; + SharedComponentDescriptorRegistry componentDescriptorRegistry_; + mutable better::optional currentAnimation_{}; + mutable std::mutex currentAnimationMutex_; + + /** + * All mutations of inflightAnimations_ are thread-safe as long as + * we keep the contract of: only mutate it within the context of + * `pullTransaction`. If that contract is held, this is implicitly protected + * by the MountingCoordinator's mutex. + */ + mutable std::vector inflightAnimations_{}; bool hasComponentDescriptorForShadowView(ShadowView const &shadowView) const; ComponentDescriptor const &getComponentDescriptorForShadowView( ShadowView const &shadowView) const; - std::pair calculateAnimationProgress( - uint64_t now, - LayoutAnimation const &animation, - AnimationConfig const &mutationConfig) const; + /** + * Given a `progress` between 0 and 1, a mutation and LayoutAnimation config, + * return a ShadowView with mutated props and/or LayoutMetrics. + * + * @param progress + * @param layoutAnimation + * @param animatedMutation + * @return + */ ShadowView createInterpolatedShadowView( double progress, - ShadowView startingView, - ShadowView finalView) const; + ShadowView const &startingView, + ShadowView const &finalView) const; void callCallback(const LayoutAnimationCallbackWrapper &callback) const; @@ -252,93 +131,66 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, ShadowViewMutation::List &mutationsList, uint64_t now) const = 0; - virtual double getProgressThroughAnimation( - AnimationKeyFrame const &keyFrame, - LayoutAnimation const *layoutAnimation, - ShadowView const &animationStateView) const = 0; - - SharedComponentDescriptorRegistry componentDescriptorRegistry_; - mutable better::optional currentAnimation_{}; - mutable std::mutex currentAnimationMutex_; - /** - * All mutations of inflightAnimations_ are thread-safe as long as - * we keep the contract of: only mutate it within the context of - * `pullTransaction`. If that contract is held, this is implicitly protected - * by the MountingCoordinator's mutex. + * Queue (and potentially synthesize) final mutations for a finished keyframe. + * Keyframe animation may have timed-out, or be canceled due to a conflict. */ - mutable std::vector inflightAnimations_{}; + void queueFinalMutationsForCompletedKeyFrame( + AnimationKeyFrame const &keyframe, + ShadowViewMutation::List &mutationsList, + bool interrupted, + const std::string &logPrefix) const; private: - // A vector of callable function wrappers that are in the process of being - // called - mutable std::mutex callbackWrappersPendingMutex_; - mutable std::vector> - callbackWrappersPending_{}; -}; + RuntimeExecutor runtimeExecutor_; + ContextContainer::Shared contextContainer_; + + mutable std::mutex layoutAnimationStatusDelegateMutex_; + mutable LayoutAnimationStatusDelegate *layoutAnimationStatusDelegate_{}; + mutable std::mutex surfaceIdsToStopMutex_; + mutable better::set surfaceIdsToStop_{}; + bool skipInvalidatedKeyFrames_{false}; + + /* + * Feature flag that forces a crash if component descriptor for shadow view + * doesn't exist. This is an unexpected state and we crash to collect extra + * logs. + */ + bool crashOnMissingComponentDescriptor_{false}; + + /* + * Feature flag that enables simulation of memory access. This is a temporary + * flag to diagnose where crashes are coming from in LayoutAnimations on iOS. + */ + bool simulateImagePropsMemoryAccess_{false}; + + // Function that returns current time in milliseconds + std::function now_; -static inline bool shouldFirstComeBeforeSecondRemovesOnly( - ShadowViewMutation const &lhs, - ShadowViewMutation const &rhs) noexcept { - // Make sure that removes on the same level are sorted - highest indices must - // come first. - return (lhs.type == ShadowViewMutation::Type::Remove && - lhs.type == rhs.type) && - (lhs.parentShadowView.tag == rhs.parentShadowView.tag) && - (lhs.index > rhs.index); -} - -static inline bool shouldFirstComeBeforeSecondMutation( - ShadowViewMutation const &lhs, - ShadowViewMutation const &rhs) noexcept { - if (lhs.type != rhs.type) { - // Deletes always come last - if (lhs.type == ShadowViewMutation::Type::Delete) { - return false; - } - if (rhs.type == ShadowViewMutation::Type::Delete) { - return true; - } - - // Update comes last, before deletes - if (rhs.type == ShadowViewMutation::Type::Update) { - return true; - } - if (lhs.type == ShadowViewMutation::Type::Update) { - return false; - } - - // Remove comes before insert - if (lhs.type == ShadowViewMutation::Type::Remove && - rhs.type == ShadowViewMutation::Type::Insert) { - return true; - } - if (rhs.type == ShadowViewMutation::Type::Remove && - lhs.type == ShadowViewMutation::Type::Insert) { - return false; - } - - // Create comes before insert - if (lhs.type == ShadowViewMutation::Type::Create && - rhs.type == ShadowViewMutation::Type::Insert) { - return true; - } - if (rhs.type == ShadowViewMutation::Type::Create && - lhs.type == ShadowViewMutation::Type::Insert) { - return false; - } - } else { - // Make sure that removes on the same level are sorted - highest indices - // must come first. - if (lhs.type == ShadowViewMutation::Type::Remove && - lhs.parentShadowView.tag == rhs.parentShadowView.tag && - lhs.index > rhs.index) { - return true; - } - } - - return false; -} + void adjustImmediateMutationIndicesForDelayedMutations( + SurfaceId surfaceId, + ShadowViewMutation &mutation, + bool skipLastAnimation = false, + bool lastAnimationOnly = false) const; + + void adjustDelayedMutationIndicesForMutation( + SurfaceId surfaceId, + ShadowViewMutation const &mutation, + bool skipLastAnimation = false) const; + + void getAndEraseConflictingAnimations( + SurfaceId surfaceId, + ShadowViewMutationList const &mutations, + std::vector &conflictingAnimations) const; + + /* + * Removes animations from `inflightAnimations_` for stopped surfaces. + */ + void deleteAnimationsForStoppedSurfaces() const; + + void simulateImagePropsMemoryAccess( + ShadowViewMutationList const &mutations) const; +}; } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/animations/conversions.h b/android/ReactCommon/react/renderer/animations/conversions.h new file mode 100644 index 0000000000000..9fdb7a5e08a68 --- /dev/null +++ b/android/ReactCommon/react/renderer/animations/conversions.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include + +namespace facebook { +namespace react { + +static inline better::optional parseAnimationType( + std::string param) { + if (param == "spring") { + return AnimationType::Spring; + } + if (param == "linear") { + return AnimationType::Linear; + } + if (param == "easeInEaseOut") { + return AnimationType::EaseInEaseOut; + } + if (param == "easeIn") { + return AnimationType::EaseIn; + } + if (param == "easeOut") { + return AnimationType::EaseOut; + } + if (param == "keyboard") { + return AnimationType::Keyboard; + } + + LOG(ERROR) << "Error parsing animation type: " << param; + return {}; +} + +static inline better::optional parseAnimationProperty( + std::string param) { + if (param == "opacity") { + return AnimationProperty::Opacity; + } + if (param == "scaleX") { + return AnimationProperty::ScaleX; + } + if (param == "scaleY") { + return AnimationProperty::ScaleY; + } + if (param == "scaleXY") { + return AnimationProperty::ScaleXY; + } + + LOG(ERROR) << "Error parsing animation property: " << param; + return {}; +} + +static inline better::optional parseAnimationConfig( + folly::dynamic const &config, + double defaultDuration, + bool parsePropertyType) { + if (config.empty() || !config.isObject()) { + return AnimationConfig{ + AnimationType::Linear, + AnimationProperty::NotApplicable, + defaultDuration, + 0, + 0, + 0}; + } + + auto const typeIt = config.find("type"); + if (typeIt == config.items().end()) { + LOG(ERROR) << "Error parsing animation config: could not find field `type`"; + return {}; + } + auto const animationTypeParam = typeIt->second; + if (animationTypeParam.empty() || !animationTypeParam.isString()) { + LOG(ERROR) + << "Error parsing animation config: could not unwrap field `type`"; + return {}; + } + const auto animationType = parseAnimationType(animationTypeParam.asString()); + if (!animationType) { + LOG(ERROR) + << "Error parsing animation config: could not parse field `type`"; + return {}; + } + + AnimationProperty animationProperty = AnimationProperty::NotApplicable; + if (parsePropertyType) { + auto const propertyIt = config.find("property"); + if (propertyIt == config.items().end()) { + LOG(ERROR) + << "Error parsing animation config: could not find field `property`"; + return {}; + } + auto const animationPropertyParam = propertyIt->second; + if (animationPropertyParam.empty() || !animationPropertyParam.isString()) { + LOG(ERROR) + << "Error parsing animation config: could not unwrap field `property`"; + return {}; + } + const auto animationPropertyParsed = + parseAnimationProperty(animationPropertyParam.asString()); + if (!animationPropertyParsed) { + LOG(ERROR) + << "Error parsing animation config: could not parse field `property`"; + return {}; + } + animationProperty = *animationPropertyParsed; + } + + double duration = defaultDuration; + double delay = 0; + double springDamping = 0.5; + double initialVelocity = 0; + + auto const durationIt = config.find("duration"); + if (durationIt != config.items().end()) { + if (durationIt->second.isDouble()) { + duration = durationIt->second.asDouble(); + } else { + LOG(ERROR) + << "Error parsing animation config: field `duration` must be a number"; + return {}; + } + } + + auto const delayIt = config.find("delay"); + if (delayIt != config.items().end()) { + if (delayIt->second.isDouble()) { + delay = delayIt->second.asDouble(); + } else { + LOG(ERROR) + << "Error parsing animation config: field `delay` must be a number"; + return {}; + } + } + + auto const springDampingIt = config.find("springDamping"); + if (springDampingIt != config.items().end() && + springDampingIt->second.isDouble()) { + if (springDampingIt->second.isDouble()) { + springDamping = springDampingIt->second.asDouble(); + } else { + LOG(ERROR) + << "Error parsing animation config: field `springDamping` must be a number"; + return {}; + } + } + + auto const initialVelocityIt = config.find("initialVelocity"); + if (initialVelocityIt != config.items().end()) { + if (initialVelocityIt->second.isDouble()) { + initialVelocity = initialVelocityIt->second.asDouble(); + } else { + LOG(ERROR) + << "Error parsing animation config: field `initialVelocity` must be a number"; + return {}; + } + } + + return better::optional(AnimationConfig{ + *animationType, + animationProperty, + duration, + delay, + springDamping, + initialVelocity}); +} + +// Parse animation config from JS +static inline better::optional +parseLayoutAnimationConfig(folly::dynamic const &config) { + if (config.empty() || !config.isObject()) { + return {}; + } + + const auto durationIt = config.find("duration"); + if (durationIt == config.items().end() || !durationIt->second.isDouble()) { + return {}; + } + const double duration = durationIt->second.asDouble(); + + const auto createConfigIt = config.find("create"); + const auto createConfig = createConfigIt == config.items().end() + ? better::optional(AnimationConfig{}) + : parseAnimationConfig(createConfigIt->second, duration, true); + + const auto updateConfigIt = config.find("update"); + const auto updateConfig = updateConfigIt == config.items().end() + ? better::optional(AnimationConfig{}) + : parseAnimationConfig(updateConfigIt->second, duration, false); + + const auto deleteConfigIt = config.find("delete"); + const auto deleteConfig = deleteConfigIt == config.items().end() + ? better::optional(AnimationConfig{}) + : parseAnimationConfig(deleteConfigIt->second, duration, true); + + if (!createConfig || !updateConfig || !deleteConfig) { + return {}; + } + + return LayoutAnimationConfig{ + duration, *createConfig, *updateConfig, *deleteConfig}; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/animations/primitives.h b/android/ReactCommon/react/renderer/animations/primitives.h new file mode 100644 index 0000000000000..757c72c466ba6 --- /dev/null +++ b/android/ReactCommon/react/renderer/animations/primitives.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +// This corresponds exactly with JS. +enum class AnimationType { + None = 0, + Spring = 1, + Linear = 2, + EaseInEaseOut = 4, + EaseIn = 8, + EaseOut = 16, + Keyboard = 32 +}; + +enum class AnimationProperty { + NotApplicable = 0, + Opacity = 1, + ScaleX = 2, + ScaleY = 4, + ScaleXY = 8 +}; + +// This corresponds exactly with JS. +struct AnimationConfig { + AnimationType animationType = AnimationType::None; + AnimationProperty animationProperty = AnimationProperty::NotApplicable; + double duration = + 0; // these are perhaps better represented as uint64_t, but they + // come from JS as doubles + double delay = 0; + double springDamping = 0; + double initialVelocity = 0; +}; + +// This corresponds exactly with JS. +struct LayoutAnimationConfig { + double duration; // ms + AnimationConfig createConfig; + AnimationConfig updateConfig; + AnimationConfig deleteConfig; +}; + +enum class AnimationConfigurationType { Create = 1, Update = 2, Delete = 4 }; + +struct AnimationKeyFrame { + // The mutation(s) that should be executed once the animation completes. + // This maybe empty. + // For CREATE/INSERT this will contain CREATE, INSERT in that order. + // For REMOVE/DELETE, same. + std::vector finalMutationsForKeyFrame; + + // The type of animation this is (for configuration purposes) + AnimationConfigurationType type; + + // Tag representing the node being animated. + Tag tag; + + ShadowView parentView; + + // ShadowView representing the start and end points of this animation. + ShadowView viewStart; + ShadowView viewEnd; + + // ShadowView representing the previous frame of the animation. + ShadowView viewPrev; + + // If an animation interrupts an existing one, the starting state may actually + // be halfway through the intended transition. + double initialProgress; + + bool invalidated{false}; + + // In the case where some mutation conflicts with this keyframe, + // should we generate final synthetic UPDATE mutations for this keyframe? + bool generateFinalSyntheticMutations{true}; +}; + +struct LayoutAnimation { + SurfaceId surfaceId; + uint64_t startTime; + bool completed = false; + LayoutAnimationConfig layoutAnimationConfig; + LayoutAnimationCallbackWrapper successCallback; + LayoutAnimationCallbackWrapper failureCallback; + std::vector keyFrames; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/animations/tests/LayoutAnimationTest.cpp b/android/ReactCommon/react/renderer/animations/tests/LayoutAnimationTest.cpp new file mode 100644 index 0000000000000..33e72268f1f68 --- /dev/null +++ b/android/ReactCommon/react/renderer/animations/tests/LayoutAnimationTest.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// Uncomment when random test blocks are uncommented below. +// #include +// #include + +#include "LayoutAnimationDriver.h" + +MockClock::time_point MockClock::time_ = {}; + +namespace facebook { +namespace react { + +static void testShadowNodeTreeLifeCycleLayoutAnimations( + uint_fast32_t seed, + int treeSize, + int repeats, + int stages, + int animation_duration, + int animation_frames, + int delay_ms_between_frames, + int delay_ms_between_stages, + int delay_ms_between_repeats, + bool commits_conflicting_mutations = false, + int final_animation_delay = 0) { + auto entropy = seed == 0 ? Entropy() : Entropy(seed); + + auto eventDispatcher = EventDispatcher::Shared{}; + auto contextContainer = std::make_shared(); + auto componentDescriptorParameters = + ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr}; + auto viewComponentDescriptor = + ViewComponentDescriptor(componentDescriptorParameters); + auto rootComponentDescriptor = + RootComponentDescriptor(componentDescriptorParameters); + auto noopEventEmitter = + std::make_shared(nullptr, -1, eventDispatcher); + + PropsParserContext parserContext{-1, *contextContainer}; + + // Create a RuntimeExecutor + RuntimeExecutor runtimeExecutor = + [](std::function fn) {}; + + // Create component descriptor registry for animation driver + auto providerRegistry = + std::make_shared(); + auto componentDescriptorRegistry = + providerRegistry->createComponentDescriptorRegistry( + componentDescriptorParameters); + providerRegistry->add( + concreteComponentDescriptorProvider()); + + // Create Animation Driver + auto animationDriver = std::make_shared( + runtimeExecutor, contextContainer, nullptr); + animationDriver->setComponentDescriptorRegistry(componentDescriptorRegistry); + + // Mock animation timers + animationDriver->setClockNow([]() { + return std::chrono::duration_cast( + MockClock::now().time_since_epoch()) + .count(); + }); + + auto allNodes = std::vector{}; + + for (int i = 0; i < repeats; i++) { + allNodes.clear(); + + int surfaceIdInt = 1; + auto surfaceId = SurfaceId(surfaceIdInt); + + auto family = rootComponentDescriptor.createFamily( + {Tag(surfaceIdInt), surfaceId, nullptr}, nullptr); + + // Creating an initial root shadow node. + auto emptyRootNode = std::const_pointer_cast( + std::static_pointer_cast( + rootComponentDescriptor.createShadowNode( + ShadowNodeFragment{RootShadowNode::defaultSharedProps()}, + family))); + + // Applying size constraints. + emptyRootNode = emptyRootNode->clone( + parserContext, + LayoutConstraints{ + Size{512, 0}, Size{512, std::numeric_limits::infinity()}}, + LayoutContext{}); + + // Generation of a random tree. + auto singleRootChildNode = + generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize); + + // Injecting a tree into the root node. + auto currentRootNode = std::static_pointer_cast( + emptyRootNode->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{singleRootChildNode})})); + + // Building an initial view hierarchy. + auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode); + viewTree.mutate( + calculateShadowViewMutations(*emptyRootNode, *currentRootNode)); + + for (int j = 0; j < stages; j++) { + auto nextRootNode = currentRootNode; + + // Mutating the tree. + alterShadowTree( + entropy, + nextRootNode, + { + &messWithChildren, + &messWithYogaStyles, + &messWithLayoutableOnlyFlag, + }); + + std::vector affectedLayoutableNodes{}; + affectedLayoutableNodes.reserve(1024); + + // Laying out the tree. + std::const_pointer_cast(nextRootNode) + ->layoutIfNeeded(&affectedLayoutableNodes); + + nextRootNode->sealRecursive(); + allNodes.push_back(nextRootNode); + + // Calculating mutations. + auto originalMutations = + calculateShadowViewMutations(*currentRootNode, *nextRootNode); + + // If tree randomization produced no changes in the form of mutations, + // don't bother trying to animate because this violates a bunch of our + // assumptions in this test + if (originalMutations.size() == 0) { + continue; + } + + // If we only mutated the root... also don't bother + if (originalMutations.size() == 1 && + (originalMutations[0].oldChildShadowView.tag == 1 || + originalMutations[0].newChildShadowView.tag == 1)) { + continue; + } + + // Configure animation + animationDriver->uiManagerDidConfigureNextLayoutAnimation( + {surfaceId, + 0, + false, + {(double)animation_duration, + {/* Create */ AnimationType::EaseInEaseOut, + AnimationProperty::Opacity, + (double)animation_duration, + 0, + 0, + 0}, + {/* Update */ AnimationType::EaseInEaseOut, + AnimationProperty::ScaleXY, + (double)animation_duration, + 0, + 0, + 0}, + {/* Delete */ AnimationType::EaseInEaseOut, + AnimationProperty::Opacity, + (double)animation_duration, + 0, + 0, + 0}}, + {}, + {}, + {}}); + + // Get mutations for each frame + for (int k = 0; k < animation_frames + 2; k++) { + auto mutationsInput = ShadowViewMutation::List{}; + if (k == 0) { + mutationsInput = originalMutations; + } + + if (k != (animation_frames + 1)) { + EXPECT_TRUE(animationDriver->shouldOverridePullTransaction()); + } else if (!commits_conflicting_mutations) { + EXPECT_FALSE(animationDriver->shouldOverridePullTransaction()); + } + + auto telemetry = TransactionTelemetry{}; + telemetry.willLayout(); + telemetry.willCommit(); + telemetry.willDiff(); + + auto transaction = animationDriver->pullTransaction( + surfaceId, 0, telemetry, mutationsInput); + + EXPECT_TRUE(transaction.has_value() || k == animation_frames); + + // We have something to validate. + if (transaction.has_value()) { + auto mutations = transaction->getMutations(); + + // Mutating the view tree. + viewTree.mutate(mutations); + + // We don't do any validation on this until all animations are + // finished! + } + + MockClock::advance_by( + std::chrono::milliseconds(delay_ms_between_frames)); + } + + // After the animation is completed... + // Build a view tree to compare with. + // After all the synthetic mutations, at the end of the animation, + // the mutated and newly-constructed trees should be identical. + if (!commits_conflicting_mutations) { + auto rebuiltViewTree = + buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode); + + // Comparing the newly built tree with the updated one. + if (rebuiltViewTree != viewTree) { + // Something went wrong. + + LOG(ERROR) + << "Entropy seed: " << entropy.getSeed() + << ". To see why trees are different, define STUB_VIEW_TREE_VERBOSE and see logging in StubViewTree.cpp.\n"; + + EXPECT_TRUE(false); + } + } + + currentRootNode = nextRootNode; + + MockClock::advance_by(std::chrono::milliseconds(delay_ms_between_stages)); + } + + // Flush all remaining animations before validating trees + if (final_animation_delay > 0) { + MockClock::advance_by(std::chrono::milliseconds(final_animation_delay)); + + auto telemetry = TransactionTelemetry{}; + telemetry.willLayout(); + telemetry.willCommit(); + telemetry.willDiff(); + + auto transaction = + animationDriver->pullTransaction(surfaceId, 0, telemetry, {}); + // We have something to validate. + if (transaction.hasValue()) { + auto mutations = transaction->getMutations(); + + // Mutating the view tree. + viewTree.mutate(mutations); + + // We don't do any validation on this until all animations are + // finished! + } + } + + // After all animations are completed... + // Build a view tree to compare with. + // After all the synthetic mutations, at the end of the animation, + // the mutated and newly-constructed trees should be identical. + if (commits_conflicting_mutations) { + auto rebuiltViewTree = + buildStubViewTreeWithoutUsingDifferentiator(*currentRootNode); + + // Comparing the newly built tree with the updated one. + if (rebuiltViewTree != viewTree) { + // Something went wrong. + + LOG(ERROR) + << "Entropy seed: " << entropy.getSeed() + << ". To see why trees are different, define STUB_VIEW_TREE_VERBOSE and see logging in StubViewTree.cpp.\n"; + + EXPECT_TRUE(false); + } + } + + MockClock::advance_by(std::chrono::milliseconds(delay_ms_between_repeats)); + } + + SUCCEED(); +} + +} // namespace react +} // namespace facebook + +using namespace facebook::react; + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_NonOverlapping_2029343357) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 2029343357, /* working seed found 5-10-2021 */ + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_NonOverlapping_3619914559) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 3619914559, /* working seed found 5-10-2021 */ + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_NonOverlapping_597132284) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 597132284, /* failing seed found 5-10-2021 */ + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_NonOverlapping_774986518) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 774986518, /* failing seed found 5-10-2021 */ + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_NonOverlapping_1450614414) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 1450614414, /* failing seed found 5-10-2021 */ + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +TEST(LayoutAnimationTest, stableBiggerTreeFewRepeatsFewStages_NonOverlapping) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 2029343357, + /* size */ 512, + /* repeats */ 32, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +TEST(LayoutAnimationTest, stableBiggerTreeFewRepeatsManyStages_NonOverlapping) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 2029343357, + /* size */ 512, + /* repeats */ 32, + /* stages */ 128, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000); +} + +// You may uncomment this - locally only! - to generate failing seeds. +// TEST(LayoutAnimationTest, stableSmallerTreeFewRepeatsFewStages_Random) { +// std::random_device device; +// for (int i = 0; i < 10; i++) { +// uint_fast32_t seed = device(); +// LOG(ERROR) << "Seed: " << seed; +// testShadowNodeTreeLifeCycleLayoutAnimations( +// /* seed */ seed, +// /* size */ 128, +// /* repeats */ 128, +// /* stages */ 10, +// /* animation_duration */ 1000, +// /* animation_frames*/ 10, +// /* delay_ms_between_frames */ 100, +// /* delay_ms_between_stages */ 100, +// /* delay_ms_between_repeats */ 2000); +// } +// // Fail if you want output to get seeds +// LOG(ERROR) << "ALL RUNS SUCCESSFUL"; +// // react_native_assert(false); +// } + +// +// These tests are "overlapping", meaning that mutations will be committed +// before the previous animation completes. +// + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_Overlapping_2029343357) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 2029343357, + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 9, // an animation completes in 10 frames, so this + // causes conflicts + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000, + /* commits_conflicting_mutations */ true, + /* final_animation_delay */ 10000 + 1); +} + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_Overlapping_597132284) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 597132284, + /* size */ 128, + /* repeats */ 128, + /* stages */ 10, + /* animation_duration */ 1000, + /* animation_frames*/ 9, // an animation completes in 10 frames, so this + // causes conflicts + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000, + /* commits_conflicting_mutations */ true, + /* final_animation_delay */ 10000 + 1); +} + +TEST( + LayoutAnimationTest, + stableSmallerTreeFewRepeatsFewStages_Overlapping_ManyConflicts_597132284) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 597132284, + /* size */ 128, + /* repeats */ 128, + /* stages */ 50, + /* animation_duration */ 1000, + /* animation_frames*/ 5, // an animation completes in 10 frames, so this + // causes conflicts. We only animate 5 frames, + // but have 50 stages, so conflicts stack up + // quickly. + /* delay_ms_between_frames */ 100, + /* delay_ms_between_stages */ 100, + /* delay_ms_between_repeats */ 2000, + /* commits_conflicting_mutations */ true, + /* final_animation_delay */ 50000 + 1); +} + +TEST( + LayoutAnimationTest, + stableBiggerTreeFewRepeatsManyStages_Overlapping_ManyConflicts_2029343357) { + testShadowNodeTreeLifeCycleLayoutAnimations( + /* seed */ 2029343357, + /* size */ 512, + /* repeats */ 32, + /* stages */ 128, + /* animation_duration */ 1000, + /* animation_frames*/ 10, + /* delay_ms_between_frames */ 10, + /* delay_ms_between_stages */ 10, + /* delay_ms_between_repeats */ 2000, + /* commits_conflicting_mutations */ true, + /* final_animation_delay */ (128 * 1000 + 100)); +} + +// You may uncomment this - +// locally only !-to generate failing seeds. +// TEST( +// LayoutAnimationTest, +// stableSmallerTreeFewRepeatsFewStages_Overlapping_Random) { +// std::random_device device; +// for (int i = 0; i < 10; i++) { +// uint_fast32_t seed = device(); +// LOG(ERROR) << "Seed: " << seed; +// testShadowNodeTreeLifeCycleLayoutAnimations( +// /* seed */ seed, +// /* size */ 512, +// /* repeats */ 32, +// /* stages */ 128, +// /* animation_duration */ 1000, +// /* animation_frames*/ 10, +// /* delay_ms_between_frames */ 10, +// /* delay_ms_between_stages */ 10, +// /* delay_ms_between_repeats */ 2000, +// /* commits_conflicting_mutations */ true, +// /* final_animation_delay */ (128 * 1000 + 100)); +// } +// // Fail if you want output to get seeds +// LOG(ERROR) << "ALL RUNS SUCCESSFUL"; +// // react_native_assert(false); +// } diff --git a/android/ReactCommon/react/renderer/animations/utils.cpp b/android/ReactCommon/react/renderer/animations/utils.cpp new file mode 100644 index 0000000000000..4d12eeab969c3 --- /dev/null +++ b/android/ReactCommon/react/renderer/animations/utils.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "utils.h" +#include + +namespace facebook { +namespace react { + +std::pair calculateAnimationProgress( + uint64_t now, + LayoutAnimation const &animation, + AnimationConfig const &mutationConfig) { + if (mutationConfig.animationType == AnimationType::None) { + return {1, 1}; + } + + uint64_t startTime = animation.startTime; + uint64_t delay = mutationConfig.delay; + uint64_t endTime = startTime + delay + mutationConfig.duration; + + if (now >= endTime) { + return {1, 1}; + } + if (now < startTime + delay) { + return {0, 0}; + } + + double linearTimeProgression = 1 - + (double)(endTime - delay - now) / (double)(endTime - animation.startTime); + + if (mutationConfig.animationType == AnimationType::Linear) { + return {linearTimeProgression, linearTimeProgression}; + } else if (mutationConfig.animationType == AnimationType::EaseIn) { + // This is an accelerator-style interpolator. + // In the future, this parameter (2.0) could be adjusted. This has been the + // default for Classic RN forever. + return {linearTimeProgression, pow(linearTimeProgression, 2.0)}; + } else if (mutationConfig.animationType == AnimationType::EaseOut) { + // This is an decelerator-style interpolator. + // In the future, this parameter (2.0) could be adjusted. This has been the + // default for Classic RN forever. + return {linearTimeProgression, 1.0 - pow(1 - linearTimeProgression, 2.0)}; + } else if (mutationConfig.animationType == AnimationType::EaseInEaseOut) { + // This is a combination of accelerate+decelerate. + // The animation starts and ends slowly, and speeds up in the middle. + return { + linearTimeProgression, + cos((linearTimeProgression + 1.0) * M_PI) / 2 + 0.5}; + } else if (mutationConfig.animationType == AnimationType::Spring) { + // Using mSpringDamping in this equation is not really the exact + // mathematical springDamping, but a good approximation We need to replace + // this equation with the right Factor that accounts for damping and + // friction + double damping = mutationConfig.springDamping; + return { + linearTimeProgression, + (1 + + pow(2, -10 * linearTimeProgression) * + sin((linearTimeProgression - damping / 4) * M_PI * 2 / damping))}; + } else { + return {linearTimeProgression, linearTimeProgression}; + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/animations/utils.h b/android/ReactCommon/react/renderer/animations/utils.h new file mode 100644 index 0000000000000..8654cea0a4ae1 --- /dev/null +++ b/android/ReactCommon/react/renderer/animations/utils.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +static inline bool shouldFirstComeBeforeSecondRemovesOnly( + ShadowViewMutation const &lhs, + ShadowViewMutation const &rhs) noexcept { + // Make sure that removes on the same level are sorted - highest indices must + // come first. + return (lhs.type == ShadowViewMutation::Type::Remove && + lhs.type == rhs.type) && + (lhs.parentShadowView.tag == rhs.parentShadowView.tag) && + (lhs.index > rhs.index); +} + +static inline bool shouldFirstComeBeforeSecondMutation( + ShadowViewMutation const &lhs, + ShadowViewMutation const &rhs) noexcept { + if (lhs.type != rhs.type) { + // Deletes always come last + if (lhs.type == ShadowViewMutation::Type::Delete) { + return false; + } + if (rhs.type == ShadowViewMutation::Type::Delete) { + return true; + } + + // Remove comes before insert + if (lhs.type == ShadowViewMutation::Type::Remove && + rhs.type == ShadowViewMutation::Type::Insert) { + return true; + } + if (rhs.type == ShadowViewMutation::Type::Remove && + lhs.type == ShadowViewMutation::Type::Insert) { + return false; + } + + // Create comes before insert + if (lhs.type == ShadowViewMutation::Type::Create && + rhs.type == ShadowViewMutation::Type::Insert) { + return true; + } + if (rhs.type == ShadowViewMutation::Type::Create && + lhs.type == ShadowViewMutation::Type::Insert) { + return false; + } + } else { + // Make sure that removes on the same level are sorted - highest indices + // must come first. + if (lhs.type == ShadowViewMutation::Type::Remove && + lhs.parentShadowView.tag == rhs.parentShadowView.tag) { + if (lhs.index > rhs.index) { + return true; + } else { + return false; + } + } + } + + return false; +} + +std::pair calculateAnimationProgress( + uint64_t now, + LayoutAnimation const &animation, + AnimationConfig const &mutationConfig); + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/attributedstring/Android.mk b/android/ReactCommon/react/renderer/attributedstring/Android.mk index 87b8da16d2333..c693f05d16ffc 100644 --- a/android/ReactCommon/react/renderer/attributedstring/Android.mk +++ b/android/ReactCommon/react/renderer/attributedstring/Android.mk @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libbetter libreact_render_graphics libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_components_view libreact_utils +LOCAL_SHARED_LIBRARIES := libbetter libreact_render_graphics libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug librrc_view libreact_utils libreact_debug libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -35,4 +35,6 @@ $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) $(call import-module,react/utils) +$(call import-module,react/debug) $(call import-module,yogajni) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/attributedstring/AttributedString.h b/android/ReactCommon/react/renderer/attributedstring/AttributedString.h index f3ed792238372..a1058c510bb26 100644 --- a/android/ReactCommon/react/renderer/attributedstring/AttributedString.h +++ b/android/ReactCommon/react/renderer/attributedstring/AttributedString.h @@ -11,7 +11,6 @@ #include #include -#include #include #include #include diff --git a/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.cpp b/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.cpp index 5983a7fc75451..a73131c9f545d 100644 --- a/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.cpp +++ b/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.cpp @@ -7,6 +7,8 @@ #include "AttributedStringBox.h" +#include + namespace facebook { namespace react { @@ -24,22 +26,42 @@ AttributedStringBox::AttributedStringBox( std::shared_ptr const &opaquePointer) : mode_(Mode::OpaquePointer), value_({}), opaquePointer_(opaquePointer) {} +AttributedStringBox::AttributedStringBox(AttributedStringBox &&other) noexcept + : mode_(other.mode_), + value_(std::move(other.value_)), + opaquePointer_(std::move(other.opaquePointer_)) { + other.mode_ = AttributedStringBox::Mode::Value; + other.value_ = std::make_shared(AttributedString{}); +} + AttributedStringBox::Mode AttributedStringBox::getMode() const { return mode_; } AttributedString const &AttributedStringBox::getValue() const { - assert(mode_ == AttributedStringBox::Mode::Value); - assert(value_); + react_native_assert(mode_ == AttributedStringBox::Mode::Value); + react_native_assert(value_); return *value_; } std::shared_ptr AttributedStringBox::getOpaquePointer() const { - assert(mode_ == AttributedStringBox::Mode::OpaquePointer); - assert(opaquePointer_); + react_native_assert(mode_ == AttributedStringBox::Mode::OpaquePointer); + react_native_assert(opaquePointer_); return opaquePointer_; } +AttributedStringBox &AttributedStringBox::operator=( + AttributedStringBox &&other) { + if (this != &other) { + mode_ = other.mode_; + value_ = std::move(other.value_); + opaquePointer_ = std::move(other.opaquePointer_); + other.mode_ = AttributedStringBox::Mode::Value; + other.value_ = std::make_shared(AttributedString{}); + } + return *this; +} + bool operator==( AttributedStringBox const &lhs, AttributedStringBox const &rhs) { @@ -48,9 +70,9 @@ bool operator==( } switch (lhs.getMode()) { - case facebook::react::AttributedStringBox::Mode::Value: + case AttributedStringBox::Mode::Value: return lhs.getValue() == rhs.getValue(); - case facebook::react::AttributedStringBox::Mode::OpaquePointer: + case AttributedStringBox::Mode::OpaquePointer: return lhs.getOpaquePointer() == rhs.getOpaquePointer(); } } diff --git a/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.h b/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.h index 7965a7d6d288f..b8bd96c9caebe 100644 --- a/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.h +++ b/android/ReactCommon/react/renderer/attributedstring/AttributedStringBox.h @@ -41,9 +41,9 @@ class AttributedStringBox final { * Movable, Copyable, Assignable. */ AttributedStringBox(AttributedStringBox const &other) = default; - AttributedStringBox(AttributedStringBox &&other) noexcept = default; + AttributedStringBox(AttributedStringBox &&other) noexcept; AttributedStringBox &operator=(AttributedStringBox const &other) = default; - AttributedStringBox &operator=(AttributedStringBox &&other) = default; + AttributedStringBox &operator=(AttributedStringBox &&other); /* * Getters. diff --git a/android/ReactCommon/react/renderer/attributedstring/BUCK b/android/ReactCommon/react/renderer/attributedstring/BUCK index 9ab399565e007..4c79645fcf3ca 100644 --- a/android/ReactCommon/react/renderer/attributedstring/BUCK +++ b/android/ReactCommon/react/renderer/attributedstring/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -31,11 +31,8 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/attributedstring", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", + fbandroid_deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), @@ -55,11 +52,12 @@ rn_xplat_cxx_library( "//xplat/folly:headers_only", "//xplat/folly:memory", "//xplat/folly:molly", - react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/graphics:graphics"), react_native_xplat_target("react/renderer/mounting:mounting"), + react_native_xplat_target("react/utils:utils"), ], ) @@ -70,10 +68,11 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], + fbandroid_use_instrumentation_test = True, platforms = (ANDROID, APPLE, CXX), deps = [ ":attributedstring", diff --git a/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.cpp b/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.cpp index fe64a462aa3e8..d5251fa907b90 100644 --- a/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.cpp +++ b/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.cpp @@ -21,13 +21,15 @@ bool ParagraphAttributes::operator==(const ParagraphAttributes &rhs) const { ellipsizeMode, textBreakStrategy, adjustsFontSizeToFit, - includeFontPadding) == + includeFontPadding, + android_hyphenationFrequency) == std::tie( rhs.maximumNumberOfLines, rhs.ellipsizeMode, rhs.textBreakStrategy, rhs.adjustsFontSizeToFit, - rhs.includeFontPadding) && + rhs.includeFontPadding, + rhs.android_hyphenationFrequency) && floatEquality(minimumFontSize, rhs.minimumFontSize) && floatEquality(maximumFontSize, rhs.maximumFontSize); } @@ -47,7 +49,9 @@ SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const { debugStringConvertibleItem("adjustsFontSizeToFit", adjustsFontSizeToFit), debugStringConvertibleItem("minimumFontSize", minimumFontSize), debugStringConvertibleItem("maximumFontSize", maximumFontSize), - debugStringConvertibleItem("includeFontPadding", includeFontPadding)}; + debugStringConvertibleItem("includeFontPadding", includeFontPadding), + debugStringConvertibleItem( + "android_hyphenationFrequency", android_hyphenationFrequency)}; } #endif diff --git a/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.h b/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.h index a5666035eff6f..22f65af3ea4dc 100644 --- a/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.h +++ b/android/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.h @@ -42,7 +42,10 @@ class ParagraphAttributes : public DebugStringConvertible { */ EllipsizeMode ellipsizeMode{}; - TextBreakStrategy textBreakStrategy{}; + /* + * (Android only) Break strategy for breaking paragraphs into lines. + */ + TextBreakStrategy textBreakStrategy{TextBreakStrategy::HighQuality}; /* * Enables font size adjustment to fit constrained boundaries. @@ -55,6 +58,12 @@ class ParagraphAttributes : public DebugStringConvertible { */ bool includeFontPadding{true}; + /* + * (Android only) Frequency of automatic hyphenation to use when determining + * word breaks. + */ + HyphenationFrequency android_hyphenationFrequency{}; + /* * In case of font size adjustment enabled, defines minimum and maximum * font sizes. @@ -89,7 +98,8 @@ struct hash { attributes.adjustsFontSizeToFit, attributes.minimumFontSize, attributes.maximumFontSize, - attributes.includeFontPadding); + attributes.includeFontPadding, + attributes.android_hyphenationFrequency); } }; } // namespace std diff --git a/android/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp b/android/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp index 5a7de8fbf9ab9..9aaea5aec5a96 100644 --- a/android/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp +++ b/android/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp @@ -50,6 +50,9 @@ void TextAttributes::apply(TextAttributes textAttributes) { letterSpacing = !std::isnan(textAttributes.letterSpacing) ? textAttributes.letterSpacing : letterSpacing; + textTransform = textAttributes.textTransform.hasValue() + ? textAttributes.textTransform + : textTransform; // Paragraph Styles lineHeight = !std::isnan(textAttributes.lineHeight) @@ -120,7 +123,8 @@ bool TextAttributes::operator==(const TextAttributes &rhs) const { textShadowColor, isHighlighted, layoutDirection, - accessibilityRole) == + accessibilityRole, + textTransform) == std::tie( rhs.foregroundColor, rhs.backgroundColor, @@ -139,7 +143,8 @@ bool TextAttributes::operator==(const TextAttributes &rhs) const { rhs.textShadowColor, rhs.isHighlighted, rhs.layoutDirection, - rhs.accessibilityRole) && + rhs.accessibilityRole, + rhs.textTransform) && floatEquality(opacity, rhs.opacity) && floatEquality(fontSize, rhs.fontSize) && floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) && diff --git a/android/ReactCommon/react/renderer/attributedstring/TextAttributes.h b/android/ReactCommon/react/renderer/attributedstring/TextAttributes.h index f15f11416f364..b7f07b962dae7 100644 --- a/android/ReactCommon/react/renderer/attributedstring/TextAttributes.h +++ b/android/ReactCommon/react/renderer/attributedstring/TextAttributes.h @@ -10,8 +10,8 @@ #include #include +#include #include -#include #include #include #include @@ -51,6 +51,7 @@ class TextAttributes : public DebugStringConvertible { better::optional fontVariant{}; better::optional allowFontScaling{}; Float letterSpacing{std::numeric_limits::quiet_NaN()}; + better::optional textTransform{}; // Paragraph Styles Float lineHeight{std::numeric_limits::quiet_NaN()}; @@ -117,6 +118,7 @@ struct hash { textAttributes.fontVariant, textAttributes.allowFontScaling, textAttributes.letterSpacing, + textAttributes.textTransform, textAttributes.lineHeight, textAttributes.alignment, textAttributes.baseWritingDirection, diff --git a/android/ReactCommon/react/renderer/attributedstring/conversions.h b/android/ReactCommon/react/renderer/attributedstring/conversions.h index a121a09028e2e..56ab6bdef9438 100644 --- a/android/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/android/ReactCommon/react/renderer/attributedstring/conversions.h @@ -9,12 +9,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -22,6 +24,11 @@ #include #include +#ifdef ANDROID +#include +#include +#endif + #include namespace facebook { @@ -38,27 +45,43 @@ inline std::string toString(const EllipsizeMode &ellipsisMode) { case EllipsizeMode::Middle: return "middle"; } + + LOG(ERROR) << "Unsupported EllipsizeMode value"; + react_native_assert(false); + + // Sane default in case of parsing errors + return "tail"; } -inline void fromRawValue(const RawValue &value, EllipsizeMode &result) { - auto string = (std::string)value; - if (string == "clip") { - result = EllipsizeMode::Clip; - return; - } - if (string == "head") { - result = EllipsizeMode::Head; - return; - } - if (string == "tail") { - result = EllipsizeMode::Tail; - return; - } - if (string == "middle") { - result = EllipsizeMode::Middle; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + EllipsizeMode &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "clip") { + result = EllipsizeMode::Clip; + } else if (string == "head") { + result = EllipsizeMode::Head; + } else if (string == "tail") { + result = EllipsizeMode::Tail; + } else if (string == "middle") { + result = EllipsizeMode::Middle; + } else { + // sane default + LOG(ERROR) << "Unsupported EllipsizeMode value: " << string; + react_native_assert(false); + result = EllipsizeMode::Tail; + } return; } - abort(); + + LOG(ERROR) << "Unsupported EllipsizeMode type"; + react_native_assert(false); + + // Sane default in case of parsing errors + result = EllipsizeMode::Tail; } inline std::string toString(const TextBreakStrategy &textBreakStrategy) { @@ -70,97 +93,114 @@ inline std::string toString(const TextBreakStrategy &textBreakStrategy) { case TextBreakStrategy::Balanced: return "balanced"; } + + LOG(ERROR) << "Unsupported TextBreakStrategy value"; + react_native_assert(false); + return "highQuality"; } -inline void fromRawValue(const RawValue &value, TextBreakStrategy &result) { - auto string = (std::string)value; - if (string == "simple") { - result = TextBreakStrategy::Simple; - return; - } - if (string == "highQuality") { - result = TextBreakStrategy::HighQuality; - return; - } - if (string == "balanced") { - result = TextBreakStrategy::Balanced; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + TextBreakStrategy &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "simple") { + result = TextBreakStrategy::Simple; + } else if (string == "highQuality") { + result = TextBreakStrategy::HighQuality; + } else if (string == "balanced") { + result = TextBreakStrategy::Balanced; + } else { + // sane default + LOG(ERROR) << "Unsupported TextBreakStrategy value: " << string; + react_native_assert(false); + result = TextBreakStrategy::HighQuality; + } return; } - abort(); + + LOG(ERROR) << "Unsupported TextBreakStrategy type"; + react_native_assert(false); + result = TextBreakStrategy::HighQuality; } -inline void fromRawValue(const RawValue &value, FontWeight &result) { - auto string = (std::string)value; - if (string == "normal") { - result = FontWeight::Regular; - return; - } - if (string == "regular") { - result = FontWeight::Regular; - return; - } - if (string == "bold") { - result = FontWeight::Bold; - return; - } - if (string == "100") { - result = FontWeight::Weight100; - return; - } - if (string == "200") { - result = FontWeight::Weight200; - return; - } - if (string == "300") { - result = FontWeight::Weight300; - return; - } - if (string == "400") { - result = FontWeight::Weight400; - return; - } - if (string == "500") { - result = FontWeight::Weight500; - return; - } - if (string == "600") { - result = FontWeight::Weight600; - return; - } - if (string == "700") { - result = FontWeight::Weight700; - return; - } - if (string == "800") { - result = FontWeight::Weight800; - return; - } - if (string == "900") { - result = FontWeight::Weight900; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + FontWeight &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "normal") { + result = FontWeight::Regular; + } else if (string == "regular") { + result = FontWeight::Regular; + } else if (string == "bold") { + result = FontWeight::Bold; + } else if (string == "100") { + result = FontWeight::Weight100; + } else if (string == "200") { + result = FontWeight::Weight200; + } else if (string == "300") { + result = FontWeight::Weight300; + } else if (string == "400") { + result = FontWeight::Weight400; + } else if (string == "500") { + result = FontWeight::Weight500; + } else if (string == "600") { + result = FontWeight::Weight600; + } else if (string == "700") { + result = FontWeight::Weight700; + } else if (string == "800") { + result = FontWeight::Weight800; + } else if (string == "900") { + result = FontWeight::Weight900; + } else { + LOG(ERROR) << "Unsupported FontWeight value: " << string; + react_native_assert(false); + // sane default for prod + result = FontWeight::Regular; + } return; } - abort(); + + LOG(ERROR) << "Unsupported FontWeight type"; + react_native_assert(false); + result = FontWeight::Regular; } inline std::string toString(const FontWeight &fontWeight) { return folly::to((int)fontWeight); } -inline void fromRawValue(const RawValue &value, FontStyle &result) { - auto string = (std::string)value; - if (string == "normal") { - result = FontStyle::Normal; - return; - } - if (string == "italic") { - result = FontStyle::Italic; - return; - } - if (string == "oblique") { - result = FontStyle::Oblique; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + FontStyle &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "normal") { + result = FontStyle::Normal; + } else if (string == "italic") { + result = FontStyle::Italic; + } else if (string == "oblique") { + result = FontStyle::Oblique; + } else { + LOG(ERROR) << "Unsupported FontStyle value: " << string; + react_native_assert(false); + // sane default for prod + result = FontStyle::Normal; + } return; } - abort(); + + LOG(ERROR) << "Unsupported FontStyle type"; + react_native_assert(false); + // sane default for prod + result = FontStyle::Normal; } inline std::string toString(const FontStyle &fontStyle) { @@ -172,33 +212,41 @@ inline std::string toString(const FontStyle &fontStyle) { case FontStyle::Oblique: return "oblique"; } + + LOG(ERROR) << "Unsupported FontStyle value"; + react_native_assert(false); + // sane default for prod + return "normal"; } -inline void fromRawValue(const RawValue &value, FontVariant &result) { - assert(value.hasType>()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + FontVariant &result) { + react_native_assert(value.hasType>()); result = FontVariant::Default; - auto items = std::vector{value}; - for (const auto &item : items) { - if (item == "small-caps") { - result = (FontVariant)((int)result | (int)FontVariant::SmallCaps); - continue; - } - if (item == "oldstyle-nums") { - result = (FontVariant)((int)result | (int)FontVariant::OldstyleNums); - continue; - } - if (item == "lining-nums") { - result = (FontVariant)((int)result | (int)FontVariant::LiningNums); - continue; - } - if (item == "tabular-nums") { - result = (FontVariant)((int)result | (int)FontVariant::TabularNums); - continue; - } - if (item == "proportional-nums") { - result = (FontVariant)((int)result | (int)FontVariant::ProportionalNums); + if (value.hasType>()) { + auto items = std::vector{value}; + for (const auto &item : items) { + if (item == "small-caps") { + result = (FontVariant)((int)result | (int)FontVariant::SmallCaps); + } else if (item == "oldstyle-nums") { + result = (FontVariant)((int)result | (int)FontVariant::OldstyleNums); + } else if (item == "lining-nums") { + result = (FontVariant)((int)result | (int)FontVariant::LiningNums); + } else if (item == "tabular-nums") { + result = (FontVariant)((int)result | (int)FontVariant::TabularNums); + } else if (item == "proportional-nums") { + result = + (FontVariant)((int)result | (int)FontVariant::ProportionalNums); + } else { + LOG(ERROR) << "Unsupported FontVariant value: " << item; + react_native_assert(false); + } continue; } + } else { + LOG(ERROR) << "Unsupported FontVariant type"; } } @@ -228,35 +276,93 @@ inline std::string toString(const FontVariant &fontVariant) { return result; } -inline void fromRawValue(const RawValue &value, TextAlignment &result) { - auto string = (std::string)value; - if (string == "auto") { - result = TextAlignment::Natural; - return; - } - if (string == "left") { - result = TextAlignment::Left; - return; - } - if (string == "center") { - result = TextAlignment::Center; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + TextTransform &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "none") { + result = TextTransform::None; + } else if (string == "uppercase") { + result = TextTransform::Uppercase; + } else if (string == "lowercase") { + result = TextTransform::Lowercase; + } else if (string == "capitalize") { + result = TextTransform::Capitalize; + } else if (string == "unset") { + result = TextTransform::Unset; + } else { + LOG(ERROR) << "Unsupported TextTransform value: " << string; + react_native_assert(false); + // sane default for prod + result = TextTransform::None; + } return; } - if (string == "right") { - result = TextAlignment::Right; - return; + + LOG(ERROR) << "Unsupported TextTransform type"; + react_native_assert(false); + // sane default for prod + result = TextTransform::None; +} + +inline std::string toString(const TextTransform &textTransform) { + switch (textTransform) { + case TextTransform::None: + return "none"; + case TextTransform::Uppercase: + return "uppercase"; + case TextTransform::Lowercase: + return "lowercase"; + case TextTransform::Capitalize: + return "capitalize"; + case TextTransform::Unset: + return "unset"; } - if (string == "justify") { - result = TextAlignment::Justified; + + LOG(ERROR) << "Unsupported TextTransform value"; + react_native_assert(false); + // sane default for prod + return "none"; +} + +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + TextAlignment &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "auto") { + result = TextAlignment::Natural; + } else if (string == "left") { + result = TextAlignment::Left; + } else if (string == "center") { + result = TextAlignment::Center; + } else if (string == "right") { + result = TextAlignment::Right; + } else if (string == "justify") { + result = TextAlignment::Justified; + } else { + LOG(ERROR) << "Unsupported TextAlignment value: " << string; + react_native_assert(false); + // sane default for prod + result = TextAlignment::Natural; + } return; } - abort(); + + LOG(ERROR) << "Unsupported TextAlignment type"; + // sane default for prod + result = TextAlignment::Natural; } inline std::string toString(const TextAlignment &textAlignment) { switch (textAlignment) { case TextAlignment::Natural: - return "natural"; + return "auto"; case TextAlignment::Left: return "left"; case TextAlignment::Center: @@ -266,62 +372,85 @@ inline std::string toString(const TextAlignment &textAlignment) { case TextAlignment::Justified: return "justified"; } + + LOG(ERROR) << "Unsupported TextAlignment value"; + // sane default for prod + return "auto"; } -inline void fromRawValue(const RawValue &value, WritingDirection &result) { - auto string = (std::string)value; - if (string == "natural") { - result = WritingDirection::Natural; - return; - } - if (string == "ltr") { - result = WritingDirection::LeftToRight; - return; - } - if (string == "rtl") { - result = WritingDirection::RightToLeft; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + WritingDirection &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "natural" || string == "auto") { + result = WritingDirection::Natural; + } else if (string == "ltr") { + result = WritingDirection::LeftToRight; + } else if (string == "rtl") { + result = WritingDirection::RightToLeft; + } else { + LOG(ERROR) << "Unsupported WritingDirection value: " << string; + react_native_assert(false); + // sane default for prod + result = WritingDirection::Natural; + } return; } - abort(); + + LOG(ERROR) << "Unsupported WritingDirection type"; + // sane default for prod + result = WritingDirection::Natural; } inline std::string toString(const WritingDirection &writingDirection) { switch (writingDirection) { case WritingDirection::Natural: - return "natural"; + return "auto"; case WritingDirection::LeftToRight: return "ltr"; case WritingDirection::RightToLeft: return "rtl"; } + + LOG(ERROR) << "Unsupported WritingDirection value"; + // sane default for prod + return "auto"; } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, TextDecorationLineType &result) { - auto string = (std::string)value; - if (string == "none") { - result = TextDecorationLineType::None; - return; - } - if (string == "underline") { - result = TextDecorationLineType::Underline; - return; - } - - // TODO: remove "line-through" after deprecation - if (string == "strikethrough" || string == "line-through") { - result = TextDecorationLineType::Strikethrough; + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "none") { + result = TextDecorationLineType::None; + } else if (string == "underline") { + result = TextDecorationLineType::Underline; + } else if (string == "strikethrough" || string == "line-through") { + // TODO: remove "line-through" after deprecation + result = TextDecorationLineType::Strikethrough; + } else if ( + string == "underline-strikethrough" || + string == "underline line-through") { + // TODO: remove "underline line-through" after "line-through" deprecation + result = TextDecorationLineType::UnderlineStrikethrough; + } else { + LOG(ERROR) << "Unsupported TextDecorationLineType value: " << string; + react_native_assert(false); + // sane default for prod + result = TextDecorationLineType::None; + } return; } - // TODO: remove "underline line-through" after "line-through" deprecation - if (string == "underline-strikethrough" || - string == "underline line-through") { - result = TextDecorationLineType::UnderlineStrikethrough; - return; - } - abort(); + LOG(ERROR) << "Unsupported TextDecorationLineType type"; + // sane default for prod + result = TextDecorationLineType::None; } inline std::string toString( @@ -336,25 +465,38 @@ inline std::string toString( case TextDecorationLineType::UnderlineStrikethrough: return "underline-strikethrough"; } + + LOG(ERROR) << "Unsupported TextDecorationLineType value"; + react_native_assert(false); + // sane default for prod + return "none"; } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, TextDecorationLineStyle &result) { - auto string = (std::string)value; - if (string == "single") { - result = TextDecorationLineStyle::Single; - return; - } - if (string == "thick") { - result = TextDecorationLineStyle::Thick; - return; - } - if (string == "double") { - result = TextDecorationLineStyle::Double; + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "single") { + result = TextDecorationLineStyle::Single; + } else if (string == "thick") { + result = TextDecorationLineStyle::Thick; + } else if (string == "double") { + result = TextDecorationLineStyle::Double; + } else { + LOG(ERROR) << "Unsupported TextDecorationLineStyle value: " << string; + react_native_assert(false); + // sane default for prod + result = TextDecorationLineStyle::Single; + } return; } - abort(); + + LOG(ERROR) << "Unsupported TextDecorationLineStyle type"; + // sane default for prod + result = TextDecorationLineStyle::Single; } inline std::string toString( @@ -367,33 +509,42 @@ inline std::string toString( case TextDecorationLineStyle::Double: return "double"; } + + LOG(ERROR) << "Unsupported TextDecorationLineStyle value"; + react_native_assert(false); + // sane default for prod + return "single"; } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, TextDecorationLinePattern &result) { - auto string = (std::string)value; - if (string == "solid") { - result = TextDecorationLinePattern::Solid; - return; - } - if (string == "dot") { - result = TextDecorationLinePattern::Dot; - return; - } - if (string == "dash") { - result = TextDecorationLinePattern::Dash; - return; - } - if (string == "dash-dot") { - result = TextDecorationLinePattern::DashDot; - return; - } - if (string == "dash-dot-dot") { - result = TextDecorationLinePattern::DashDotDot; + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "solid") { + result = TextDecorationLinePattern::Solid; + } else if (string == "dot") { + result = TextDecorationLinePattern::Dot; + } else if (string == "dash") { + result = TextDecorationLinePattern::Dash; + } else if (string == "dash-dot") { + result = TextDecorationLinePattern::DashDot; + } else if (string == "dash-dot-dot") { + result = TextDecorationLinePattern::DashDotDot; + } else { + LOG(ERROR) << "Unsupported TextDecorationLinePattern value: " << string; + react_native_assert(false); + // sane default for prod + result = TextDecorationLinePattern::Solid; + } return; } - abort(); + + LOG(ERROR) << "Unsupported TextDecorationLineStyle type"; + // sane default for prod + result = TextDecorationLinePattern::Solid; } inline std::string toString( @@ -410,6 +561,11 @@ inline std::string toString( case TextDecorationLinePattern::DashDotDot: return "dash-dot-dot"; } + + LOG(ERROR) << "Unsupported TextDecorationLinePattern value"; + react_native_assert(false); + // sane default for prod + return "solid"; } inline std::string toString(const AccessibilityRole &accessibilityRole) { @@ -462,6 +618,8 @@ inline std::string toString(const AccessibilityRole &accessibilityRole) { return "switch"; case AccessibilityRole::Tab: return "tab"; + case AccessibilityRole::TabBar: + return "tabbar"; case AccessibilityRole::Tablist: return "tablist"; case AccessibilityRole::Timer: @@ -469,167 +627,194 @@ inline std::string toString(const AccessibilityRole &accessibilityRole) { case AccessibilityRole::Toolbar: return "toolbar"; } + + LOG(ERROR) << "Unsupported AccessibilityRole value"; + react_native_assert(false); + // sane default for prod + return "none"; } -inline void fromRawValue(const RawValue &value, AccessibilityRole &result) { - auto string = (std::string)value; - if (string == "none") { - result = AccessibilityRole::None; - return; - } - if (string == "button") { - result = AccessibilityRole::Button; - return; - } - if (string == "link") { - result = AccessibilityRole::Link; - return; - } - if (string == "search") { - result = AccessibilityRole::Search; - return; - } - if (string == "image") { - result = AccessibilityRole::Image; - return; - } - if (string == "imagebutton") { - result = AccessibilityRole::Imagebutton; - return; - } - if (string == "keyboardkey") { - result = AccessibilityRole::Keyboardkey; - return; - } - if (string == "text") { - result = AccessibilityRole::Text; - return; - } - if (string == "adjustable") { - result = AccessibilityRole::Adjustable; - return; - } - if (string == "summary") { - result = AccessibilityRole::Summary; - return; - } - if (string == "header") { - result = AccessibilityRole::Header; - return; - } - if (string == "alert") { - result = AccessibilityRole::Alert; - return; - } - if (string == "checkbox") { - result = AccessibilityRole::Checkbox; - return; - } - if (string == "combobox") { - result = AccessibilityRole::Combobox; - return; - } - if (string == "menu") { - result = AccessibilityRole::Menu; - return; - } - if (string == "menubar") { - result = AccessibilityRole::Menubar; - return; - } - if (string == "menuitem") { - result = AccessibilityRole::Menuitem; - return; - } - if (string == "progressbar") { - result = AccessibilityRole::Progressbar; - return; - } - if (string == "radio") { - result = AccessibilityRole::Radio; - return; - } - if (string == "radiogroup") { - result = AccessibilityRole::Radiogroup; - return; - } - if (string == "scrollbar") { - result = AccessibilityRole::Scrollbar; - return; - } - if (string == "spinbutton") { - result = AccessibilityRole::Spinbutton; - return; - } - if (string == "switch") { - result = AccessibilityRole::Switch; - return; - } - if (string == "tab") { - result = AccessibilityRole::Tab; - return; - } - if (string == "tablist") { - result = AccessibilityRole::Tablist; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + AccessibilityRole &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "none") { + result = AccessibilityRole::None; + } else if (string == "button") { + result = AccessibilityRole::Button; + } else if (string == "link") { + result = AccessibilityRole::Link; + } else if (string == "search") { + result = AccessibilityRole::Search; + } else if (string == "image") { + result = AccessibilityRole::Image; + } else if (string == "imagebutton") { + result = AccessibilityRole::Imagebutton; + } else if (string == "keyboardkey") { + result = AccessibilityRole::Keyboardkey; + } else if (string == "text") { + result = AccessibilityRole::Text; + } else if (string == "adjustable") { + result = AccessibilityRole::Adjustable; + } else if (string == "summary") { + result = AccessibilityRole::Summary; + } else if (string == "header") { + result = AccessibilityRole::Header; + } else if (string == "alert") { + result = AccessibilityRole::Alert; + } else if (string == "checkbox") { + result = AccessibilityRole::Checkbox; + } else if (string == "combobox") { + result = AccessibilityRole::Combobox; + } else if (string == "menu") { + result = AccessibilityRole::Menu; + } else if (string == "menubar") { + result = AccessibilityRole::Menubar; + } else if (string == "menuitem") { + result = AccessibilityRole::Menuitem; + } else if (string == "progressbar") { + result = AccessibilityRole::Progressbar; + } else if (string == "radio") { + result = AccessibilityRole::Radio; + } else if (string == "radiogroup") { + result = AccessibilityRole::Radiogroup; + } else if (string == "scrollbar") { + result = AccessibilityRole::Scrollbar; + } else if (string == "spinbutton") { + result = AccessibilityRole::Spinbutton; + } else if (string == "switch") { + result = AccessibilityRole::Switch; + } else if (string == "tab") { + result = AccessibilityRole::Tab; + } else if (string == "tabbar") { + result = AccessibilityRole::TabBar; + } else if (string == "tablist") { + result = AccessibilityRole::Tablist; + } else if (string == "timer") { + result = AccessibilityRole::Timer; + } else if (string == "toolbar") { + result = AccessibilityRole::Toolbar; + } else { + LOG(ERROR) << "Unsupported AccessibilityRole value: " << string; + react_native_assert(false); + // sane default for prod + result = AccessibilityRole::None; + } return; } - if (string == "timer") { - result = AccessibilityRole::Timer; - return; + + LOG(ERROR) << "Unsupported AccessibilityRole type"; + react_native_assert(false); + // sane default for prod + result = AccessibilityRole::None; +} + +inline std::string toString(const HyphenationFrequency &hyphenationFrequency) { + switch (hyphenationFrequency) { + case HyphenationFrequency::None: + return "none"; + case HyphenationFrequency::Normal: + return "normal"; + case HyphenationFrequency::Full: + return "full"; } - if (string == "toolbar") { - result = AccessibilityRole::Toolbar; + + LOG(ERROR) << "Unsupported HyphenationFrequency value"; + react_native_assert(false); + return "none"; +} + +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + HyphenationFrequency &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "none") { + result = HyphenationFrequency::None; + } else if (string == "normal") { + result = HyphenationFrequency::Normal; + } else if (string == "full") { + result = HyphenationFrequency::Full; + } else { + // sane default + LOG(ERROR) << "Unsupported HyphenationFrequency value: " << string; + react_native_assert(false); + result = HyphenationFrequency::None; + } return; } - abort(); + + LOG(ERROR) << "Unsupported HyphenationFrequency type"; + react_native_assert(false); + result = HyphenationFrequency::None; } inline ParagraphAttributes convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, ParagraphAttributes const &sourceParagraphAttributes, ParagraphAttributes const &defaultParagraphAttributes) { auto paragraphAttributes = ParagraphAttributes{}; paragraphAttributes.maximumNumberOfLines = convertRawProp( + context, rawProps, "numberOfLines", sourceParagraphAttributes.maximumNumberOfLines, defaultParagraphAttributes.maximumNumberOfLines); paragraphAttributes.ellipsizeMode = convertRawProp( + context, rawProps, "ellipsizeMode", sourceParagraphAttributes.ellipsizeMode, defaultParagraphAttributes.ellipsizeMode); paragraphAttributes.textBreakStrategy = convertRawProp( + context, rawProps, "textBreakStrategy", sourceParagraphAttributes.textBreakStrategy, defaultParagraphAttributes.textBreakStrategy); paragraphAttributes.adjustsFontSizeToFit = convertRawProp( + context, rawProps, "adjustsFontSizeToFit", sourceParagraphAttributes.adjustsFontSizeToFit, defaultParagraphAttributes.adjustsFontSizeToFit); paragraphAttributes.minimumFontSize = convertRawProp( + context, rawProps, "minimumFontSize", sourceParagraphAttributes.minimumFontSize, defaultParagraphAttributes.minimumFontSize); paragraphAttributes.maximumFontSize = convertRawProp( + context, rawProps, "maximumFontSize", sourceParagraphAttributes.maximumFontSize, defaultParagraphAttributes.maximumFontSize); paragraphAttributes.includeFontPadding = convertRawProp( + context, rawProps, "includeFontPadding", sourceParagraphAttributes.includeFontPadding, defaultParagraphAttributes.includeFontPadding); + paragraphAttributes.android_hyphenationFrequency = convertRawProp( + context, + rawProps, + "android_hyphenationFrequency", + sourceParagraphAttributes.android_hyphenationFrequency, + defaultParagraphAttributes.android_hyphenationFrequency); return paragraphAttributes; } inline void fromRawValue( + const PropsParserContext &context, RawValue const &value, AttributedString::Range &result) { auto map = (better::map)value; @@ -659,6 +844,9 @@ inline folly::dynamic toDynamic( values("textBreakStrategy", toString(paragraphAttributes.textBreakStrategy)); values("adjustsFontSizeToFit", paragraphAttributes.adjustsFontSizeToFit); values("includeFontPadding", paragraphAttributes.includeFontPadding); + values( + "android_hyphenationFrequency", + toString(paragraphAttributes.android_hyphenationFrequency)); return values; } @@ -721,6 +909,9 @@ inline folly::dynamic toDynamic(const TextAttributes &textAttributes) { if (!std::isnan(textAttributes.letterSpacing)) { _textAttributes("letterSpacing", textAttributes.letterSpacing); } + if (textAttributes.textTransform.hasValue()) { + _textAttributes("textTransform", toString(*textAttributes.textTransform)); + } if (!std::isnan(textAttributes.lineHeight)) { _textAttributes("lineHeight", textAttributes.lineHeight); } @@ -808,6 +999,228 @@ inline folly::dynamic toDynamic(AttributedString::Range const &range) { return dynamicValue; } +// constants for AttributedString serialization +constexpr static Key AS_KEY_HASH = 0; +constexpr static Key AS_KEY_STRING = 1; +constexpr static Key AS_KEY_FRAGMENTS = 2; +constexpr static Key AS_KEY_CACHE_ID = 3; + +// constants for Fragment serialization +constexpr static Key FR_KEY_STRING = 0; +constexpr static Key FR_KEY_REACT_TAG = 1; +constexpr static Key FR_KEY_IS_ATTACHMENT = 2; +constexpr static Key FR_KEY_WIDTH = 3; +constexpr static Key FR_KEY_HEIGHT = 4; +constexpr static Key FR_KEY_TEXT_ATTRIBUTES = 5; + +// constants for Text Attributes serialization +constexpr static Key TA_KEY_FOREGROUND_COLOR = 0; +constexpr static Key TA_KEY_BACKGROUND_COLOR = 1; +constexpr static Key TA_KEY_OPACITY = 2; +constexpr static Key TA_KEY_FONT_FAMILY = 3; +constexpr static Key TA_KEY_FONT_SIZE = 4; +constexpr static Key TA_KEY_FONT_SIZE_MULTIPLIER = 5; +constexpr static Key TA_KEY_FONT_WEIGHT = 6; +constexpr static Key TA_KEY_FONT_STYLE = 7; +constexpr static Key TA_KEY_FONT_VARIANT = 8; +constexpr static Key TA_KEY_ALLOW_FONT_SCALING = 9; +constexpr static Key TA_KEY_LETTER_SPACING = 10; +constexpr static Key TA_KEY_LINE_HEIGHT = 11; +constexpr static Key TA_KEY_ALIGNMENT = 12; +constexpr static Key TA_KEY_BEST_WRITING_DIRECTION = 13; +constexpr static Key TA_KEY_TEXT_DECORATION_COLOR = 14; +constexpr static Key TA_KEY_TEXT_DECORATION_LINE = 15; +constexpr static Key TA_KEY_TEXT_DECORATION_LINE_STYLE = 16; +constexpr static Key TA_KEY_TEXT_DECORATION_LINE_PATTERN = 17; +constexpr static Key TA_KEY_TEXT_SHADOW_RAIDUS = 18; +constexpr static Key TA_KEY_TEXT_SHADOW_COLOR = 19; +constexpr static Key TA_KEY_IS_HIGHLIGHTED = 20; +constexpr static Key TA_KEY_LAYOUT_DIRECTION = 21; +constexpr static Key TA_KEY_ACCESSIBILITY_ROLE = 22; + +// constants for ParagraphAttributes serialization +constexpr static Key PA_KEY_MAX_NUMBER_OF_LINES = 0; +constexpr static Key PA_KEY_ELLIPSIZE_MODE = 1; +constexpr static Key PA_KEY_TEXT_BREAK_STRATEGY = 2; +constexpr static Key PA_KEY_ADJUST_FONT_SIZE_TO_FIT = 3; +constexpr static Key PA_KEY_INCLUDE_FONT_PADDING = 4; +constexpr static Key PA_KEY_HYPHENATION_FREQUENCY = 5; + +inline MapBuffer toMapBuffer(const ParagraphAttributes ¶graphAttributes) { + auto builder = MapBufferBuilder(); + builder.putInt( + PA_KEY_MAX_NUMBER_OF_LINES, paragraphAttributes.maximumNumberOfLines); + builder.putString( + PA_KEY_ELLIPSIZE_MODE, toString(paragraphAttributes.ellipsizeMode)); + builder.putString( + PA_KEY_TEXT_BREAK_STRATEGY, + toString(paragraphAttributes.textBreakStrategy)); + builder.putBool( + PA_KEY_ADJUST_FONT_SIZE_TO_FIT, paragraphAttributes.adjustsFontSizeToFit); + builder.putBool( + PA_KEY_INCLUDE_FONT_PADDING, paragraphAttributes.includeFontPadding); + builder.putString( + PA_KEY_HYPHENATION_FREQUENCY, + toString(paragraphAttributes.android_hyphenationFrequency)); + + return builder.build(); +} + +inline MapBuffer toMapBuffer(const FontVariant &fontVariant) { + auto builder = MapBufferBuilder(); + int index = 0; + if ((int)fontVariant & (int)FontVariant::SmallCaps) { + builder.putString(index++, "small-caps"); + } + if ((int)fontVariant & (int)FontVariant::OldstyleNums) { + builder.putString(index++, "oldstyle-nums"); + } + if ((int)fontVariant & (int)FontVariant::LiningNums) { + builder.putString(index++, "lining-nums"); + } + if ((int)fontVariant & (int)FontVariant::TabularNums) { + builder.putString(index++, "tabular-nums"); + } + if ((int)fontVariant & (int)FontVariant::ProportionalNums) { + builder.putString(index++, "proportional-nums"); + } + + return builder.build(); +} + +inline MapBuffer toMapBuffer(const TextAttributes &textAttributes) { + auto builder = MapBufferBuilder(); + if (textAttributes.foregroundColor) { + builder.putInt( + TA_KEY_FOREGROUND_COLOR, toMapBuffer(textAttributes.foregroundColor)); + } + if (textAttributes.backgroundColor) { + builder.putInt( + TA_KEY_BACKGROUND_COLOR, toMapBuffer(textAttributes.backgroundColor)); + } + if (!std::isnan(textAttributes.opacity)) { + builder.putDouble(TA_KEY_OPACITY, textAttributes.opacity); + } + if (!textAttributes.fontFamily.empty()) { + builder.putString(TA_KEY_FONT_FAMILY, textAttributes.fontFamily); + } + if (!std::isnan(textAttributes.fontSize)) { + builder.putDouble(TA_KEY_FONT_SIZE, textAttributes.fontSize); + } + if (!std::isnan(textAttributes.fontSizeMultiplier)) { + builder.putDouble( + TA_KEY_FONT_SIZE_MULTIPLIER, textAttributes.fontSizeMultiplier); + } + if (textAttributes.fontWeight.has_value()) { + builder.putString(TA_KEY_FONT_WEIGHT, toString(*textAttributes.fontWeight)); + } + if (textAttributes.fontStyle.has_value()) { + builder.putString(TA_KEY_FONT_STYLE, toString(*textAttributes.fontStyle)); + } + if (textAttributes.fontVariant.has_value()) { + auto fontVariantMap = toMapBuffer(*textAttributes.fontVariant); + builder.putMapBuffer(TA_KEY_FONT_VARIANT, fontVariantMap); + } + if (textAttributes.allowFontScaling.has_value()) { + builder.putBool( + TA_KEY_ALLOW_FONT_SCALING, *textAttributes.allowFontScaling); + } + if (!std::isnan(textAttributes.letterSpacing)) { + builder.putDouble(TA_KEY_LETTER_SPACING, textAttributes.letterSpacing); + } + if (!std::isnan(textAttributes.lineHeight)) { + builder.putDouble(TA_KEY_LINE_HEIGHT, textAttributes.lineHeight); + } + if (textAttributes.alignment.has_value()) { + builder.putString(TA_KEY_ALIGNMENT, toString(*textAttributes.alignment)); + } + if (textAttributes.baseWritingDirection.has_value()) { + builder.putString( + TA_KEY_BEST_WRITING_DIRECTION, + toString(*textAttributes.baseWritingDirection)); + } + // Decoration + if (textAttributes.textDecorationColor) { + builder.putInt( + TA_KEY_TEXT_DECORATION_COLOR, + toMapBuffer(textAttributes.textDecorationColor)); + } + if (textAttributes.textDecorationLineType.has_value()) { + builder.putString( + TA_KEY_TEXT_DECORATION_LINE, + toString(*textAttributes.textDecorationLineType)); + } + if (textAttributes.textDecorationLineStyle.has_value()) { + builder.putString( + TA_KEY_TEXT_DECORATION_LINE_STYLE, + toString(*textAttributes.textDecorationLineStyle)); + } + if (textAttributes.textDecorationLinePattern.has_value()) { + builder.putString( + TA_KEY_TEXT_DECORATION_LINE_PATTERN, + toString(*textAttributes.textDecorationLinePattern)); + } + // Shadow + if (!std::isnan(textAttributes.textShadowRadius)) { + builder.putDouble( + TA_KEY_TEXT_SHADOW_RAIDUS, textAttributes.textShadowRadius); + } + if (textAttributes.textShadowColor) { + builder.putInt( + TA_KEY_TEXT_SHADOW_COLOR, toMapBuffer(textAttributes.textShadowColor)); + } + // Special + if (textAttributes.isHighlighted.has_value()) { + builder.putBool(TA_KEY_IS_HIGHLIGHTED, *textAttributes.isHighlighted); + } + if (textAttributes.layoutDirection.has_value()) { + builder.putString( + TA_KEY_LAYOUT_DIRECTION, toString(*textAttributes.layoutDirection)); + } + if (textAttributes.accessibilityRole.has_value()) { + builder.putString( + TA_KEY_ACCESSIBILITY_ROLE, toString(*textAttributes.accessibilityRole)); + } + return builder.build(); +} + +inline MapBuffer toMapBuffer(const AttributedString &attributedString) { + auto fragmentsBuilder = MapBufferBuilder(); + + int index = 0; + for (auto fragment : attributedString.getFragments()) { + auto dynamicFragmentBuilder = MapBufferBuilder(); + dynamicFragmentBuilder.putString(FR_KEY_STRING, fragment.string); + if (fragment.parentShadowView.componentHandle) { + dynamicFragmentBuilder.putInt( + FR_KEY_REACT_TAG, fragment.parentShadowView.tag); + } + if (fragment.isAttachment()) { + dynamicFragmentBuilder.putBool(FR_KEY_IS_ATTACHMENT, true); + dynamicFragmentBuilder.putDouble( + FR_KEY_WIDTH, + fragment.parentShadowView.layoutMetrics.frame.size.width); + dynamicFragmentBuilder.putDouble( + FR_KEY_HEIGHT, + fragment.parentShadowView.layoutMetrics.frame.size.height); + } + auto textAttributesMap = toMapBuffer(fragment.textAttributes); + dynamicFragmentBuilder.putMapBuffer( + FR_KEY_TEXT_ATTRIBUTES, textAttributesMap); + auto dynamicFragmentMap = dynamicFragmentBuilder.build(); + fragmentsBuilder.putMapBuffer(index++, dynamicFragmentMap); + } + + auto builder = MapBufferBuilder(); + builder.putInt( + AS_KEY_HASH, + std::hash{}(attributedString)); + builder.putString(AS_KEY_STRING, attributedString.getString()); + auto fragmentsMap = fragmentsBuilder.build(); + builder.putMapBuffer(AS_KEY_FRAGMENTS, fragmentsMap); + return builder.build(); +} + #endif } // namespace react diff --git a/android/ReactCommon/react/renderer/attributedstring/primitives.h b/android/ReactCommon/react/renderer/attributedstring/primitives.h index 13946ec180fc8..d4be52366132d 100644 --- a/android/ReactCommon/react/renderer/attributedstring/primitives.h +++ b/android/ReactCommon/react/renderer/attributedstring/primitives.h @@ -53,7 +53,11 @@ enum class EllipsizeMode { Middle // Truncate middle of line: "ab...yz". }; -enum class TextBreakStrategy { Simple, Balanced, HighQuality }; +enum class TextBreakStrategy { + Simple, // Simple strategy. + HighQuality, // High-quality strategy, including hyphenation. + Balanced // Balances line lengths. +}; enum class TextAlignment { Natural, // Indicates the default alignment for script. @@ -112,11 +116,26 @@ enum class AccessibilityRole { Spinbutton, Switch, Tab, + TabBar, Tablist, Timer, Toolbar, }; +enum class TextTransform { + None, + Uppercase, + Lowercase, + Capitalize, + Unset, +}; + +enum class HyphenationFrequency { + None, // No hyphenation. + Normal, // Less frequent hyphenation. + Full // Standard amount of hyphenation. +}; + } // namespace react } // namespace facebook @@ -197,4 +216,18 @@ struct hash { return hash()(static_cast(v)); } }; + +template <> +struct hash { + size_t operator()(const facebook::react::TextTransform &v) const { + return hash()(static_cast(v)); + } +}; + +template <> +struct hash { + size_t operator()(const facebook::react::HyphenationFrequency &v) const { + return hash()(static_cast(v)); + } +}; } // namespace std diff --git a/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringBoxTest.cpp b/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringBoxTest.cpp new file mode 100644 index 0000000000000..493598b2c42cb --- /dev/null +++ b/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringBoxTest.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +namespace facebook { +namespace react { + +TEST(AttributedStringBoxTest, testDefaultConstructor) { + auto attributedStringBox = AttributedStringBox{}; + + EXPECT_EQ(attributedStringBox.getMode(), AttributedStringBox::Mode::Value); + EXPECT_EQ(attributedStringBox.getValue(), AttributedString{}); +} + +TEST(AttributedStringBoxTest, testValueConstructor) { + auto attributedString = AttributedString{}; + auto fragment = AttributedString::Fragment{}; + fragment.string = "test string"; + attributedString.appendFragment(fragment); + auto attributedStringBox = AttributedStringBox{attributedString}; + + EXPECT_EQ(attributedStringBox.getMode(), AttributedStringBox::Mode::Value); + EXPECT_EQ(attributedStringBox.getValue(), attributedString); +} + +TEST(AttributedStringBoxTest, testOpaquePointerConstructor) { + auto string = std::make_shared("test string"); + auto attributedStringBox = AttributedStringBox{string}; + + EXPECT_EQ( + attributedStringBox.getMode(), AttributedStringBox::Mode::OpaquePointer); + EXPECT_EQ(attributedStringBox.getOpaquePointer(), string); + EXPECT_EQ(string.use_count(), 2); +} + +TEST(AttributedStringBoxTest, testMoveConstructor) { + { + auto string = std::make_shared("test string"); + auto movedFromAttributedStringBox = AttributedStringBox{string}; + + auto moveToAttributedStringBox = + AttributedStringBox{std::move(movedFromAttributedStringBox)}; + + EXPECT_EQ( + moveToAttributedStringBox.getMode(), + AttributedStringBox::Mode::OpaquePointer); + EXPECT_EQ(moveToAttributedStringBox.getOpaquePointer(), string); + EXPECT_EQ(string.use_count(), 2); + } + { + auto attributedString = AttributedString{}; + auto fragment = AttributedString::Fragment{}; + fragment.string = "test string"; + attributedString.appendFragment(fragment); + auto movedFromAttributedStringBox = AttributedStringBox{attributedString}; + + auto moveToAttributedStringBox = + AttributedStringBox{std::move(movedFromAttributedStringBox)}; + + EXPECT_EQ( + moveToAttributedStringBox.getMode(), AttributedStringBox::Mode::Value); + EXPECT_EQ(moveToAttributedStringBox.getValue(), attributedString); + } +} + +TEST(AttributedStringBoxTest, testMoveAssignment) { + { + auto string = std::make_shared("test string"); + auto movedFromAttributedStringBox = AttributedStringBox{string}; + + auto movedToAttributedStringBox = AttributedStringBox{}; + movedToAttributedStringBox = std::move(movedFromAttributedStringBox); + + EXPECT_EQ( + movedToAttributedStringBox.getMode(), + AttributedStringBox::Mode::OpaquePointer); + EXPECT_EQ(movedToAttributedStringBox.getOpaquePointer(), string); + EXPECT_EQ(string.use_count(), 2); + } + { + auto attributedString = AttributedString{}; + auto fragment = AttributedString::Fragment{}; + fragment.string = "test string"; + attributedString.appendFragment(fragment); + auto movedFromAttributedStringBox = AttributedStringBox{attributedString}; + + auto moveToAttributedStringBox = AttributedStringBox{}; + moveToAttributedStringBox = std::move(movedFromAttributedStringBox); + + EXPECT_EQ( + moveToAttributedStringBox.getMode(), AttributedStringBox::Mode::Value); + EXPECT_EQ(moveToAttributedStringBox.getValue(), attributedString); + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringTest.cpp b/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringTest.cpp index 9e84969713d08..f0dff66859265 100644 --- a/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringTest.cpp +++ b/android/ReactCommon/react/renderer/attributedstring/tests/AttributedStringTest.cpp @@ -5,9 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -#include - -#include #include #include #include @@ -20,28 +17,28 @@ namespace react { #ifdef ANDROID TEST(AttributedStringTest, testToDynamic) { - auto attString = new AttributedString(); - auto fragment = new AttributedString::Fragment(); - fragment->string = "test"; + auto attributedString = AttributedString{}; + auto fragment = AttributedString::Fragment{}; + fragment.string = "test"; - auto text = new TextAttributes(); - text->foregroundColor = { + auto text = TextAttributes{}; + text.foregroundColor = { colorFromComponents({100 / 255.0, 153 / 255.0, 200 / 255.0, 1.0})}; - text->opacity = 0.5; - text->fontStyle = FontStyle::Italic; - text->fontWeight = FontWeight::Thin; - text->fontVariant = FontVariant::TabularNums; - fragment->textAttributes = *text; + text.opacity = 0.5; + text.fontStyle = FontStyle::Italic; + text.fontWeight = FontWeight::Thin; + text.fontVariant = FontVariant::TabularNums; + fragment.textAttributes = text; - attString->prependFragment(*fragment); + attributedString.appendFragment(fragment); - auto result = toDynamic(*attString); - assert(result["string"] == fragment->string); + auto result = toDynamic(attributedString); + EXPECT_EQ(result["string"], fragment.string); auto textAttribute = result["fragments"][0]["textAttributes"]; - assert(textAttribute["foregroundColor"] == toDynamic(text->foregroundColor)); - assert(textAttribute["opacity"] == text->opacity); - assert(textAttribute["fontStyle"] == toString(*text->fontStyle)); - assert(textAttribute["fontWeight"] == toString(*text->fontWeight)); + EXPECT_EQ(textAttribute["foregroundColor"], toDynamic(text.foregroundColor)); + EXPECT_EQ(textAttribute["opacity"], text.opacity); + EXPECT_EQ(textAttribute["fontStyle"], toString(text.fontStyle.value())); + EXPECT_EQ(textAttribute["fontWeight"], toString(text.fontWeight.value())); } #endif diff --git a/android/ReactCommon/react/renderer/attributedstring/tests/ParagraphAttributesTest.cpp b/android/ReactCommon/react/renderer/attributedstring/tests/ParagraphAttributesTest.cpp index 56378937062f1..8b44e8b9fc2b9 100644 --- a/android/ReactCommon/react/renderer/attributedstring/tests/ParagraphAttributesTest.cpp +++ b/android/ReactCommon/react/renderer/attributedstring/tests/ParagraphAttributesTest.cpp @@ -5,9 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -#include - -#include #include #include #include @@ -19,20 +16,18 @@ namespace react { #ifdef ANDROID TEST(ParagraphAttributesTest, testToDynamic) { - auto paragraphAttributes = ParagraphAttributes(); + auto paragraphAttributes = ParagraphAttributes{}; paragraphAttributes.maximumNumberOfLines = 2; paragraphAttributes.adjustsFontSizeToFit = false; paragraphAttributes.ellipsizeMode = EllipsizeMode::Middle; auto result = toDynamic(paragraphAttributes); - assert( - result["maximumNumberOfLines"] == - paragraphAttributes.maximumNumberOfLines); - assert( - result["adjustsFontSizeToFit"] == - paragraphAttributes.adjustsFontSizeToFit); - assert( - result["ellipsizeMode"] == toString(paragraphAttributes.ellipsizeMode)); + EXPECT_EQ( + result["maximumNumberOfLines"], paragraphAttributes.maximumNumberOfLines); + EXPECT_EQ( + result["adjustsFontSizeToFit"], paragraphAttributes.adjustsFontSizeToFit); + EXPECT_EQ( + result["ellipsizeMode"], toString(paragraphAttributes.ellipsizeMode)); } #endif diff --git a/android/ReactCommon/react/renderer/attributedstring/tests/TextAttributesTest.cpp b/android/ReactCommon/react/renderer/attributedstring/tests/TextAttributesTest.cpp index 7c25e0c7278ad..7fce2b9a7835b 100644 --- a/android/ReactCommon/react/renderer/attributedstring/tests/TextAttributesTest.cpp +++ b/android/ReactCommon/react/renderer/attributedstring/tests/TextAttributesTest.cpp @@ -5,9 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -#include - -#include #include #include #include @@ -20,19 +17,20 @@ namespace react { #ifdef ANDROID TEST(TextAttributesTest, testToDynamic) { - auto text = TextAttributes(); - text.foregroundColor = { + auto textAttributes = TextAttributes{}; + textAttributes.foregroundColor = { colorFromComponents({200 / 255.0, 153 / 255.0, 100 / 255.0, 1.0})}; - text.opacity = 0.5; - text.fontStyle = FontStyle::Italic; - text.fontWeight = FontWeight::Thin; - text.fontVariant = FontVariant::TabularNums; - - auto result = toDynamic(text); - assert(result["foregroundColor"] == toDynamic(text.foregroundColor)); - assert(result["opacity"] == text.opacity); - assert(result["fontStyle"] == toString(*text.fontStyle)); - assert(result["fontWeight"] == toString(*text.fontWeight)); + textAttributes.opacity = 0.5; + textAttributes.fontStyle = FontStyle::Italic; + textAttributes.fontWeight = FontWeight::Thin; + textAttributes.fontVariant = FontVariant::TabularNums; + + auto result = toDynamic(textAttributes); + EXPECT_EQ( + result["foregroundColor"], toDynamic(textAttributes.foregroundColor)); + EXPECT_EQ(result["opacity"], textAttributes.opacity); + EXPECT_EQ(result["fontStyle"], toString(textAttributes.fontStyle.value())); + EXPECT_EQ(result["fontWeight"], toString(textAttributes.fontWeight.value())); } #endif diff --git a/android/ReactCommon/react/renderer/componentregistry/Android.mk b/android/ReactCommon/react/renderer/componentregistry/Android.mk index 3ca9726aa4d92..06297c7298058 100644 --- a/android/ReactCommon/react/renderer/componentregistry/Android.mk +++ b/android/ReactCommon/react/renderer/componentregistry/Android.mk @@ -15,12 +15,12 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ -LOCAL_SHARED_LIBRARIES := libjsi libfolly_futures libfolly_json libreact_render_core libreact_render_debug libreact_utils libglog_init +LOCAL_SHARED_LIBRARIES := libjsi libfolly_futures libfolly_json libreact_render_core libreact_render_debug libreact_utils libglog_init libreact_debug LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) @@ -30,3 +30,4 @@ $(call import-module,jsi) $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/utils) +$(call import-module,react/debug) diff --git a/android/ReactCommon/react/renderer/componentregistry/BUCK b/android/ReactCommon/react/renderer/componentregistry/BUCK index 7fa21e963dda4..4ffce4a8be3d7 100644 --- a/android/ReactCommon/react/renderer/componentregistry/BUCK +++ b/android/ReactCommon/react/renderer/componentregistry/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -6,6 +5,7 @@ load( "CXX", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -16,11 +16,11 @@ APPLE_COMPILER_FLAGS = get_apple_compiler_flags() rn_xplat_cxx_library( name = "componentregistry", srcs = glob( - ["**/*.cpp"], + ["*.cpp"], exclude = glob(["tests/**/*.cpp"]), ), headers = glob( - ["**/*.h"], + ["*.h"], exclude = glob(["tests/**/*.h"]), ), header_namespace = "", @@ -30,12 +30,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/componentregistry", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -55,6 +49,7 @@ rn_xplat_cxx_library( "//xplat/folly:molly", "//xplat/jsi:JSIDynamic", "//xplat/jsi:jsi", + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/utils:utils"), diff --git a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProvider.h b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProvider.h index c321d4af91691..99e91a8e9a7c2 100644 --- a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProvider.h +++ b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProvider.h @@ -63,10 +63,11 @@ ComponentDescriptorProvider concreteComponentDescriptorProvider() { std::is_base_of::value, "ComponentDescriptorT must be a descendant of ComponentDescriptor"); - return {ComponentDescriptorT::ConcreteShadowNode::Handle(), - ComponentDescriptorT::ConcreteShadowNode::Name(), - nullptr, - &concreteComponentDescriptorConstructor}; + return { + ComponentDescriptorT::ConcreteShadowNode::Handle(), + ComponentDescriptorT::ConcreteShadowNode::Name(), + nullptr, + &concreteComponentDescriptorConstructor}; } } // namespace react diff --git a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.cpp b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.cpp index 4255db72157a0..609316369d39c 100644 --- a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.cpp +++ b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.cpp @@ -68,8 +68,8 @@ ComponentDescriptorProviderRegistry::createComponentDescriptorRegistry( ComponentDescriptorParameters const ¶meters) const { std::shared_lock lock(mutex_); - auto registry = - std::make_shared(parameters, *this); + auto registry = std::make_shared( + parameters, *this, parameters.contextContainer); for (auto const &pair : componentDescriptorProviders_) { registry->add(pair.second); diff --git a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h index 769f0f3b91c7e..c4d1982b0b079 100644 --- a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h +++ b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace facebook { namespace react { diff --git a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp index ede6ca809372e..137fb20cbbfe4 100644 --- a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp +++ b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp @@ -7,7 +7,11 @@ #include "ComponentDescriptorRegistry.h" +#include "componentNameByReactViewName.h" + +#include #include +#include #include namespace facebook { @@ -15,8 +19,11 @@ namespace react { ComponentDescriptorRegistry::ComponentDescriptorRegistry( ComponentDescriptorParameters const ¶meters, - ComponentDescriptorProviderRegistry const &providerRegistry) - : parameters_(parameters), providerRegistry_(providerRegistry) {} + ComponentDescriptorProviderRegistry const &providerRegistry, + ContextContainer::Shared contextContainer) + : parameters_(parameters), + providerRegistry_(providerRegistry), + contextContainer_(contextContainer) {} void ComponentDescriptorRegistry::add( ComponentDescriptorProvider componentDescriptorProvider) const { @@ -26,10 +33,10 @@ void ComponentDescriptorRegistry::add( {parameters_.eventDispatcher, parameters_.contextContainer, componentDescriptorProvider.flavor}); - assert( + react_native_assert( componentDescriptor->getComponentHandle() == componentDescriptorProvider.handle); - assert( + react_native_assert( componentDescriptor->getComponentName() == componentDescriptorProvider.name); @@ -49,65 +56,6 @@ void ComponentDescriptorRegistry::registerComponentDescriptor( _registryByName[componentName] = componentDescriptor; } -static std::string componentNameByReactViewName(std::string viewName) { - // We need this function only for the transition period; - // eventually, all names will be unified. - - std::string rctPrefix("RCT"); - if (std::mismatch(rctPrefix.begin(), rctPrefix.end(), viewName.begin()) - .first == rctPrefix.end()) { - // If `viewName` has "RCT" prefix, remove it. - viewName.erase(0, rctPrefix.length()); - } - - // Fabric uses slightly new names for Text components because of differences - // in semantic. - if (viewName == "Text") { - return "Paragraph"; - } - - // TODO T63839307: remove this condition after deleting TextInlineImage from - // Paper - if (viewName == "TextInlineImage") { - return "Image"; - } - if (viewName == "VirtualText") { - return "Text"; - } - - if (viewName == "ImageView") { - return "Image"; - } - - if (viewName == "AndroidHorizontalScrollView") { - return "ScrollView"; - } - - if (viewName == "RKShimmeringView") { - return "ShimmeringView"; - } - - if (viewName == "RefreshControl") { - return "PullToRefreshView"; - } - - // We need this temporarily for testing purposes until we have proper - // implementation of core components. - if (viewName == "ScrollContentView" || - viewName == "AndroidHorizontalScrollContentView" // Android - ) { - return "View"; - } - - // iOS-only - if (viewName == "MultilineTextInputView" || - viewName == "SinglelineTextInputView") { - return "TextInput"; - } - - return viewName; -} - ComponentDescriptor const &ComponentDescriptorRegistry::at( std::string const &componentName) const { std::shared_lock lock(mutex_); @@ -116,9 +64,9 @@ ComponentDescriptor const &ComponentDescriptorRegistry::at( auto it = _registryByName.find(unifiedComponentName); if (it == _registryByName.end()) { - mutex_.unlock_shared(); + lock.unlock(); providerRegistry_.request(unifiedComponentName.c_str()); - mutex_.lock_shared(); + lock.lock(); it = _registryByName.find(unifiedComponentName); @@ -185,11 +133,13 @@ SharedShadowNode ComponentDescriptorRegistry::createNode( auto unifiedComponentName = componentNameByReactViewName(viewName); auto const &componentDescriptor = this->at(unifiedComponentName); - auto family = componentDescriptor.createFamily( - ShadowNodeFamilyFragment{tag, surfaceId, nullptr}, - std::move(eventTarget)); - auto const props = - componentDescriptor.cloneProps(nullptr, RawProps(propsDynamic)); + auto const fragment = ShadowNodeFamilyFragment{tag, surfaceId, nullptr}; + auto family = + componentDescriptor.createFamily(fragment, std::move(eventTarget)); + auto const props = componentDescriptor.cloneProps( + PropsParserContext{surfaceId, *contextContainer_.get()}, + nullptr, + RawProps(propsDynamic)); auto const state = componentDescriptor.createInitialState(ShadowNodeFragment{props}, family); diff --git a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.h b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.h index 29dac6f371ef1..e23d41c146165 100644 --- a/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.h +++ b/android/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.h @@ -14,6 +14,7 @@ #include #include +#include namespace facebook { namespace react { @@ -37,7 +38,8 @@ class ComponentDescriptorRegistry { */ ComponentDescriptorRegistry( ComponentDescriptorParameters const ¶meters, - ComponentDescriptorProviderRegistry const &providerRegistry); + ComponentDescriptorProviderRegistry const &providerRegistry, + ContextContainer::Shared contextContainer); /* * This is broken. Please do not use. @@ -85,6 +87,7 @@ class ComponentDescriptorRegistry { ComponentDescriptor::Shared _fallbackComponentDescriptor; ComponentDescriptorParameters parameters_{}; ComponentDescriptorProviderRegistry const &providerRegistry_; + ContextContainer::Shared contextContainer_; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp b/android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp new file mode 100644 index 0000000000000..d703dcb549d9c --- /dev/null +++ b/android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "componentNameByReactViewName.h" + +namespace facebook { +namespace react { + +std::string componentNameByReactViewName(std::string viewName) { + // We need this function only for the transition period; + // eventually, all names will be unified. + + // TODO T97384889: unify component names between JS - Android - iOS - C++ + std::string rctPrefix("RCT"); + if (std::mismatch(rctPrefix.begin(), rctPrefix.end(), viewName.begin()) + .first == rctPrefix.end()) { + // If `viewName` has "RCT" prefix, remove it. + viewName.erase(0, rctPrefix.length()); + } + + // Fabric uses slightly new names for Text components because of differences + // in semantic. + if (viewName == "Text") { + return "Paragraph"; + } + + // TODO T63839307: remove this condition after deleting TextInlineImage from + // old renderer code + if (viewName == "TextInlineImage") { + return "Image"; + } + if (viewName == "VirtualText") { + return "Text"; + } + + if (viewName == "ImageView") { + return "Image"; + } + + if (viewName == "AndroidHorizontalScrollView") { + return "ScrollView"; + } + + if (viewName == "RKShimmeringView") { + return "ShimmeringView"; + } + + if (viewName == "RefreshControl") { + return "PullToRefreshView"; + } + + // We need this temporarily for testing purposes until we have proper + // implementation of core components. + // iOS-only + if (viewName == "ScrollContentView") { + return "View"; + } + + // iOS-only + if (viewName == "MultilineTextInputView" || + viewName == "SinglelineTextInputView") { + return "TextInput"; + } + + return viewName; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.cpp b/android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.h similarity index 62% rename from android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.cpp rename to android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.h index 2d39cdb106b85..8408b2a2cadc0 100644 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.cpp +++ b/android/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.h @@ -5,12 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -#include "PickerShadowNode.h" +#pragma once + +#include namespace facebook { namespace react { -extern const char PickerComponentName[] = "Picker"; +/** + * Provides mapping from old view name format to the new format. + */ +std::string componentNameByReactViewName(std::string viewName); } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/componentregistry/native/Android.mk b/android/ReactCommon/react/renderer/componentregistry/native/Android.mk new file mode 100644 index 0000000000000..f5674482f6bc4 --- /dev/null +++ b/android/ReactCommon/react/renderer/componentregistry/native/Android.mk @@ -0,0 +1,29 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := rrc_native + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../ + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ + +LOCAL_SHARED_LIBRARIES := libjsi libfolly_futures libfolly_json libreact_render_core libreact_render_debug libreact_utils libglog_init libreact_debug + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"Fabric\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,fbgloginit) +$(call import-module,folly) +$(call import-module,jsi) diff --git a/android/ReactCommon/react/renderer/componentregistry/native/BUCK b/android/ReactCommon/react/renderer/componentregistry/native/BUCK new file mode 100644 index 0000000000000..a6aceadb76866 --- /dev/null +++ b/android/ReactCommon/react/renderer/componentregistry/native/BUCK @@ -0,0 +1,52 @@ +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "native", + srcs = glob( + ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), + ), + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/renderer/componentregistry/native", + ), + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + visibility = ["PUBLIC"], + deps = [ + "//third-party/glog:glog", + "//xplat/fbsystrace:fbsystrace", + "//xplat/folly:headers_only", + "//xplat/folly:memory", + "//xplat/folly:molly", + "//xplat/jsi:JSIDynamic", + "//xplat/jsi:jsi", + ], +) diff --git a/android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.cpp b/android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.cpp new file mode 100644 index 0000000000000..4748776630496 --- /dev/null +++ b/android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "NativeComponentRegistryBinding.h" + +#include +#include + +using namespace facebook; + +namespace facebook { +namespace react { + +/** + * Public API to install the NativeComponentRegistryBinding. + */ +NativeComponentRegistryBinding::NativeComponentRegistryBinding( + const HasComponentProviderFunctionType &&hasComponentProvider) + : hasComponentProvider_(std::move(hasComponentProvider)) {} + +void NativeComponentRegistryBinding::install( + jsi::Runtime &runtime, + const HasComponentProviderFunctionType &&hasComponentProvider) { + runtime.global().setProperty( + runtime, + "__nativeComponentRegistry__hasComponent", + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii( + runtime, "__nativeComponentRegistry__hasComponent"), + 1, + [binding = std::make_shared( + std::move(hasComponentProvider))]( + jsi::Runtime &rt, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count) { + return binding->jsProxy(rt, thisVal, args, count); + })); +} + +bool NativeComponentRegistryBinding::hasComponent(const std::string &name) { + return hasComponentProvider_(name); +} + +jsi::Value NativeComponentRegistryBinding::jsProxy( + jsi::Runtime &runtime, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count) { + if (count != 1) { + throw std::invalid_argument( + "__nativeComponentRegistry__hasComponent must be called with 1 argument"); + } + std::string moduleName = args[0].getString(runtime).utf8(runtime); + jsi::Value nullSchema = jsi::Value::undefined(); + + bool result = hasComponent(moduleName); + + return jsi::Value(result); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.h b/android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.h new file mode 100644 index 0000000000000..403b332342df7 --- /dev/null +++ b/android/ReactCommon/react/renderer/componentregistry/native/NativeComponentRegistryBinding.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +namespace facebook { +namespace react { + +/** + * An app/platform-specific provider function to determine if a component + * is registered in the native platform. + */ +using HasComponentProviderFunctionType = + std::function; + +/** + * Represents the JavaScript binding for the HasComponent global function. + */ +class NativeComponentRegistryBinding { + public: + /* + * Installs NativeComponentRegistryBinding into JavaScript runtime. + * Thread synchronization must be enforced externally. + */ + static void install( + jsi::Runtime &runtime, + const HasComponentProviderFunctionType &&provider); + + NativeComponentRegistryBinding( + const HasComponentProviderFunctionType &&provider); + + /** + * Returns if there's a component registered with the name received as a + * parameter + */ + bool hasComponent(const std::string &name); + + private: + /** + * A lookup function exposed to JS to determine if a component is registered + * in the native platform. + */ + jsi::Value jsProxy( + jsi::Runtime &runtime, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count); + + HasComponentProviderFunctionType hasComponentProvider_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/image/Android.mk b/android/ReactCommon/react/renderer/components/image/Android.mk index 1d0875ec4247b..8c3fbcabda9c0 100644 --- a/android/ReactCommon/react/renderer/components/image/Android.mk +++ b/android/ReactCommon/react/renderer/components/image/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_image +LOCAL_MODULE := rrc_image LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view libreact_render_imagemanager +LOCAL_SHARED_LIBRARIES := libjsi libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_render_imagemanager libreact_debug libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -34,3 +34,5 @@ $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/imagemanager) $(call import-module,react/renderer/components/view) $(call import-module,yogajni) +$(call import-module,react/debug) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/components/image/BUCK b/android/ReactCommon/react/renderer/components/image/BUCK index 205771229aea1..36ad159d49ef9 100644 --- a/android/ReactCommon/react/renderer/components/image/BUCK +++ b/android/ReactCommon/react/renderer/components/image/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -29,11 +29,8 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/image", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", + fbandroid_deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), @@ -50,6 +47,7 @@ rn_xplat_cxx_library( "//xplat/folly:headers_only", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/graphics:graphics"), @@ -65,7 +63,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/image/ImageComponentDescriptor.h b/android/ReactCommon/react/renderer/components/image/ImageComponentDescriptor.h index 1e2b6708649ff..39465a0fe6107 100644 --- a/android/ReactCommon/react/renderer/components/image/ImageComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/image/ImageComponentDescriptor.h @@ -25,10 +25,9 @@ class ImageComponentDescriptor final : ConcreteComponentDescriptor(parameters), imageManager_(std::make_shared(contextContainer_)){}; - void adopt(UnsharedShadowNode shadowNode) const override { + void adopt(ShadowNode::Unshared const &shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); - assert(std::dynamic_pointer_cast(shadowNode)); auto imageShadowNode = std::static_pointer_cast(shadowNode); diff --git a/android/ReactCommon/react/renderer/components/image/ImageProps.cpp b/android/ReactCommon/react/renderer/components/image/ImageProps.cpp index 4a496bba12d01..b007b4869d458 100644 --- a/android/ReactCommon/react/renderer/components/image/ImageProps.cpp +++ b/android/ReactCommon/react/renderer/components/image/ImageProps.cpp @@ -12,26 +12,45 @@ namespace facebook { namespace react { -ImageProps::ImageProps(const ImageProps &sourceProps, const RawProps &rawProps) - : ViewProps(sourceProps, rawProps), - sources(convertRawProp(rawProps, "source", sourceProps.sources, {})), +ImageProps::ImageProps( + const PropsParserContext &context, + const ImageProps &sourceProps, + const RawProps &rawProps) + : ViewProps(context, sourceProps, rawProps), + sources( + convertRawProp(context, rawProps, "source", sourceProps.sources, {})), defaultSources(convertRawProp( + context, rawProps, "defaultSource", sourceProps.defaultSources, {})), resizeMode(convertRawProp( + context, rawProps, "resizeMode", sourceProps.resizeMode, ImageResizeMode::Stretch)), - blurRadius( - convertRawProp(rawProps, "blurRadius", sourceProps.blurRadius, {})), - capInsets( - convertRawProp(rawProps, "capInsets", sourceProps.capInsets, {})), - tintColor( - convertRawProp(rawProps, "tintColor", sourceProps.tintColor, {})), + blurRadius(convertRawProp( + context, + rawProps, + "blurRadius", + sourceProps.blurRadius, + {})), + capInsets(convertRawProp( + context, + rawProps, + "capInsets", + sourceProps.capInsets, + {})), + tintColor(convertRawProp( + context, + rawProps, + "tintColor", + sourceProps.tintColor, + {})), internal_analyticTag(convertRawProp( + context, rawProps, "internal_analyticTag", sourceProps.internal_analyticTag, diff --git a/android/ReactCommon/react/renderer/components/image/ImageProps.h b/android/ReactCommon/react/renderer/components/image/ImageProps.h index 7ecfc27b8f714..b3d9ef36cf53b 100644 --- a/android/ReactCommon/react/renderer/components/image/ImageProps.h +++ b/android/ReactCommon/react/renderer/components/image/ImageProps.h @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -16,7 +17,10 @@ namespace react { class ImageProps final : public ViewProps { public: ImageProps() = default; - ImageProps(const ImageProps &sourceProps, const RawProps &rawProps); + ImageProps( + const PropsParserContext &context, + const ImageProps &sourceProps, + const RawProps &rawProps); #pragma mark - Props diff --git a/android/ReactCommon/react/renderer/components/image/ImageShadowNode.cpp b/android/ReactCommon/react/renderer/components/image/ImageShadowNode.cpp index fe20bc78845db..64a1020e2e217 100644 --- a/android/ReactCommon/react/renderer/components/image/ImageShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/image/ImageShadowNode.cpp @@ -25,7 +25,7 @@ void ImageShadowNode::setImageManager(const SharedImageManager &imageManager) { void ImageShadowNode::updateStateIfNeeded() { ensureUnsealed(); - auto const &imageSource = getImageSource(); + auto imageSource = getImageSource(); auto const ¤tState = getStateData(); bool hasSameRadius = getConcreteProps().blurRadius == currentState.getBlurRadius(); @@ -35,10 +35,10 @@ void ImageShadowNode::updateStateIfNeeded() { return; } - auto state = - ImageState{imageSource, - imageManager_->requestImage(imageSource, getSurfaceId()), - getConcreteProps().blurRadius}; + auto state = ImageState{ + imageSource, + imageManager_->requestImage(imageSource, getSurfaceId()), + getConcreteProps().blurRadius}; setStateData(std::move(state)); } diff --git a/android/ReactCommon/react/renderer/components/image/ImageState.h b/android/ReactCommon/react/renderer/components/image/ImageState.h index 8dfa5d6f90217..68cdcb681f4fd 100644 --- a/android/ReactCommon/react/renderer/components/image/ImageState.h +++ b/android/ReactCommon/react/renderer/components/image/ImageState.h @@ -7,10 +7,14 @@ #pragma once -#include #include #include +#ifdef ANDROID +#include +#include +#endif + namespace facebook { namespace react { @@ -50,6 +54,10 @@ class ImageState final { folly::dynamic getDynamic() const { return {}; }; + + MapBuffer getMapBuffer() const { + return MapBufferBuilder::EMPTY(); + }; #endif private: diff --git a/android/ReactCommon/react/renderer/components/image/conversions.h b/android/ReactCommon/react/renderer/components/image/conversions.h index a77d40fad489e..9449dfe773b27 100644 --- a/android/ReactCommon/react/renderer/components/image/conversions.h +++ b/android/ReactCommon/react/renderer/components/image/conversions.h @@ -9,13 +9,19 @@ #include #include +#include +#include +#include #include #include namespace facebook { namespace react { -inline void fromRawValue(const RawValue &value, ImageSource &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + ImageSource &result) { if (value.hasType()) { result = { /* .type = */ ImageSource::Type::Remote, @@ -49,7 +55,7 @@ inline void fromRawValue(const RawValue &value, ImageSource &result) { items.at("scale").hasType()) { result.scale = (Float)items.at("scale"); } else { - result.scale = items.find("deprecated") != items.end() ? 0.0 : 1.0; + result.scale = items.find("deprecated") != items.end() ? 0.0f : 1.0f; } if (items.find("url") != items.end() && @@ -87,30 +93,35 @@ inline std::string toString(const ImageSource &value) { return "{uri: " + value.uri + "}"; } -inline void fromRawValue(const RawValue &value, ImageResizeMode &result) { - assert(value.hasType()); - auto stringValue = (std::string)value; - if (stringValue == "cover") { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + ImageResizeMode &result) { + react_native_assert(value.hasType()); + if (!value.hasType()) { + LOG(ERROR) << "Unsupported ImageResizeMode type"; + // "cover" is default in non-Fabric web and iOS result = ImageResizeMode::Cover; return; } - if (stringValue == "contain") { + + auto stringValue = (std::string)value; + if (stringValue == "cover") { + result = ImageResizeMode::Cover; + } else if (stringValue == "contain") { result = ImageResizeMode::Contain; - return; - } - if (stringValue == "stretch") { + } else if (stringValue == "stretch") { result = ImageResizeMode::Stretch; - return; - } - if (stringValue == "center") { + } else if (stringValue == "center") { result = ImageResizeMode::Center; - return; - } - if (stringValue == "repeat") { + } else if (stringValue == "repeat") { result = ImageResizeMode::Repeat; - return; + } else { + LOG(ERROR) << "Unsupported ImageResizeMode value: " << stringValue; + react_native_assert(false); + // "cover" is default in non-Fabric web and iOS + result = ImageResizeMode::Cover; } - abort(); } inline std::string toString(const ImageResizeMode &value) { diff --git a/android/ReactCommon/react/renderer/components/inputaccessory/BUCK b/android/ReactCommon/react/renderer/components/inputaccessory/BUCK index c1debab5958b0..f50bb4337bffe 100644 --- a/android/ReactCommon/react/renderer/components/inputaccessory/BUCK +++ b/android/ReactCommon/react/renderer/components/inputaccessory/BUCK @@ -1,9 +1,9 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "APPLE", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -24,23 +24,18 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/inputaccessory", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], - platforms = (APPLE), + platforms = APPLE, preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", "-DWITH_FBSYSTRACE=1", ], visibility = ["PUBLIC"], deps = [ + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/core:core"), "//xplat/js/react-native-github:generated_components-rncore", ], diff --git a/android/ReactCommon/react/renderer/components/inputaccessory/InputAccessoryComponentDescriptor.h b/android/ReactCommon/react/renderer/components/inputaccessory/InputAccessoryComponentDescriptor.h index 99898b2395bae..7978f06330afc 100644 --- a/android/ReactCommon/react/renderer/components/inputaccessory/InputAccessoryComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/inputaccessory/InputAccessoryComponentDescriptor.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -21,13 +22,10 @@ class InputAccessoryComponentDescriptor final public: using ConcreteComponentDescriptor::ConcreteComponentDescriptor; - void adopt(UnsharedShadowNode shadowNode) const override { - assert(std::dynamic_pointer_cast(shadowNode)); + void adopt(ShadowNode::Unshared const &shadowNode) const override { auto concreteShadowNode = std::static_pointer_cast(shadowNode); - assert(std::dynamic_pointer_cast( - concreteShadowNode)); auto layoutableShadowNode = std::static_pointer_cast(concreteShadowNode); diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/BUCK b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/BUCK index 6a76b1098695f..6a5f2da93f54e 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/BUCK +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/BUCK @@ -1,9 +1,9 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "APPLE", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -27,17 +27,11 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/legacyviewmanagerinterop", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], - platforms = (APPLE), + platforms = APPLE, preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", "-DWITH_FBSYSTRACE=1", diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.h b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.h index 67d1291a10ef4..089e3ff40ccce 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.h @@ -28,7 +28,7 @@ class LegacyViewManagerInteropComponentDescriptor final ComponentName getComponentName() const override; protected: - void adopt(ShadowNode::Unshared shadowNode) const override; + void adopt(ShadowNode::Unshared const &shadowNode) const override; private: std::shared_ptr const _coordinator; diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm index 06f79b844afbe..8c44bc2465383 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm @@ -8,8 +8,8 @@ #include "LegacyViewManagerInteropComponentDescriptor.h" #include #include +#include #include -#include #include #include #include "LegacyViewManagerInteropState.h" @@ -59,8 +59,21 @@ auto moduleName = moduleNameFromComponentName(componentName); Class module = NSClassFromString(RCTNSStringFromString(moduleName)); assert(module); - RCTBridge *bridge = (RCTBridge *)unwrapManagedObjectWeakly(contextContainer->at>("Bridge")); - RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:module bridge:bridge]; + auto optionalBridge = contextContainer->find>("Bridge"); + RCTBridge *bridge; + if (optionalBridge) { + bridge = unwrapManagedObjectWeakly(optionalBridge.value()); + } + + auto optionalEventDispatcher = contextContainer->find>("RCTEventDispatcher"); + RCTEventDispatcher *eventDispatcher; + if (optionalEventDispatcher) { + eventDispatcher = unwrapManagedObject(optionalEventDispatcher.value()); + } + + RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:module + bridge:bridge + eventDispatcher:eventDispatcher]; return wrapManagedObject([[RCTLegacyViewManagerInteropCoordinator alloc] initWithComponentData:componentData bridge:bridge]); } @@ -81,7 +94,7 @@ return std::static_pointer_cast(this->flavor_)->c_str(); } -void LegacyViewManagerInteropComponentDescriptor::adopt(ShadowNode::Unshared shadowNode) const +void LegacyViewManagerInteropComponentDescriptor::adopt(ShadowNode::Unshared const &shadowNode) const { ConcreteComponentDescriptor::adopt(shadowNode); diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.cpp b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.cpp index 891c0e0aecce1..4676a2c01e1b6 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.cpp +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.cpp @@ -6,14 +6,19 @@ */ #include "LegacyViewManagerInteropViewProps.h" +#include namespace facebook { namespace react { LegacyViewManagerInteropViewProps::LegacyViewManagerInteropViewProps( + const PropsParserContext &context, const LegacyViewManagerInteropViewProps &sourceProps, const RawProps &rawProps) - : ViewProps(sourceProps, rawProps), otherProps((folly::dynamic)rawProps) {} + : ViewProps(context, sourceProps, rawProps), + otherProps( + mergeDynamicProps(sourceProps.otherProps, (folly::dynamic)rawProps)) { +} } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.h b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.h index 9b56571e579b6..b2645cd7de2d3 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.h +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewProps.h @@ -7,6 +7,7 @@ #include #include +#include #include namespace facebook { @@ -16,6 +17,7 @@ class LegacyViewManagerInteropViewProps final : public ViewProps { public: LegacyViewManagerInteropViewProps() = default; LegacyViewManagerInteropViewProps( + const PropsParserContext &context, const LegacyViewManagerInteropViewProps &sourceProps, const RawProps &rawProps); diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h index f93f959283946..ca84249cae994 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h @@ -20,7 +20,7 @@ typedef void (^InterceptorBlock)(std::string eventName, folly::dynamic event); - (instancetype)initWithComponentData:(RCTComponentData *)componentData bridge:(RCTBridge *)bridge; -- (UIView *)paperView; +- (UIView *)createPaperViewWithTag:(NSInteger)tag; - (void)addObserveForTag:(NSInteger)tag usingBlock:(InterceptorBlock)block; diff --git a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index 3e962334bf9c7..60ac9786f6594 100644 --- a/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/android/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -12,10 +12,13 @@ #include #include #include +#include #include #include #include +#include #include +#include using namespace facebook::react; @@ -23,13 +26,20 @@ @implementation RCTLegacyViewManagerInteropCoordinator { RCTComponentData *_componentData; __weak RCTBridge *_bridge; /* - Each instnace of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched. + Each instance of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched. This is the container that maps unretained UIView pointer to a block to which the event is dispatched. */ NSMutableDictionary *_eventInterceptors; + + /* + * In bridgeless mode, instead of using the bridge to look up RCTModuleData, + * store that information locally. + */ + NSMutableArray> *_moduleMethods; + NSMutableDictionary> *_moduleMethodsByName; } -- (instancetype)initWithComponentData:(RCTComponentData *)componentData bridge:(RCTBridge *)bridge; +- (instancetype)initWithComponentData:(RCTComponentData *)componentData bridge:(RCTBridge *)bridge { if (self = [super init]) { _componentData = componentData; @@ -40,9 +50,11 @@ - (instancetype)initWithComponentData:(RCTComponentData *)componentData bridge:( __weak __typeof(self) weakSelf = self; _componentData.eventInterceptor = ^(NSString *eventName, NSDictionary *event, NSNumber *reactTag) { __typeof(self) strongSelf = weakSelf; - InterceptorBlock block = [strongSelf->_eventInterceptors objectForKey:reactTag]; - if (block) { - block(std::string([RCTNormalizeInputEventName(eventName) UTF8String]), convertIdToFollyDynamic(event ?: @{})); + if (strongSelf) { + InterceptorBlock block = [strongSelf->_eventInterceptors objectForKey:reactTag]; + if (block) { + block(std::string([RCTNormalizeInputEventName(eventName) UTF8String]), convertIdToFollyDynamic(event ?: @{})); + } } }; } @@ -59,10 +71,17 @@ - (void)removeObserveForTag:(NSInteger)tag [_eventInterceptors removeObjectForKey:[NSNumber numberWithInteger:tag]]; } -- (UIView *)paperView +- (UIView *)createPaperViewWithTag:(NSInteger)tag; { - // TODO: pass in the right tags? - return [_componentData createViewWithTag:NULL rootTag:NULL]; + UIView *view = [_componentData createViewWithTag:[NSNumber numberWithInteger:tag] rootTag:NULL]; + if ([_componentData.bridgelessViewManager conformsToProtocol:@protocol(RCTWeakViewHolder)]) { + id weakViewHolder = (id)_componentData.bridgelessViewManager; + if (!weakViewHolder.weakViews) { + weakViewHolder.weakViews = [NSMapTable strongToWeakObjectsMapTable]; + } + [weakViewHolder.weakViews setObject:view forKey:[NSNumber numberWithInteger:tag]]; + } + return view; } - (void)setProps:(folly::dynamic const &)props forView:(UIView *)view @@ -79,12 +98,13 @@ - (NSString *)componentViewName - (void)handleCommand:(NSString *)commandName args:(NSArray *)args reactTag:(NSInteger)tag { Class managerClass = _componentData.managerClass; + [self _lookupModuleMethodsIfNecessary]; RCTModuleData *moduleData = [_bridge.batchedBridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)]; id method; if ([commandName isKindOfClass:[NSNumber class]]) { - method = moduleData.methods[[commandName intValue]]; + method = moduleData ? moduleData.methods[[commandName intValue]] : _moduleMethods[[commandName intValue]]; } else if ([commandName isKindOfClass:[NSString class]]) { - method = moduleData.methodsByName[commandName]; + method = moduleData ? moduleData.methodsByName[commandName] : _moduleMethodsByName[commandName]; if (method == nil) { RCTLogError(@"No command found with name \"%@\"", commandName); } @@ -94,12 +114,51 @@ - (void)handleCommand:(NSString *)commandName args:(NSArray *)args reactTag:(NSI } NSArray *newArgs = [@[ [NSNumber numberWithInteger:tag] ] arrayByAddingObjectsFromArray:args]; - [_bridge.batchedBridge - dispatchBlock:^{ - [method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs]; - [self->_bridge.uiManager setNeedsLayout]; + + if (_bridge) { + [_bridge.batchedBridge + dispatchBlock:^{ + [method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs]; + [self->_bridge.uiManager setNeedsLayout]; + } + queue:RCTGetUIManagerQueue()]; + } else { + // TODO T86826778 - Figure out which queue this should be dispatched to. + [method invokeWithBridge:nil module:self->_componentData.manager arguments:newArgs]; + } +} + +#pragma mark - Private + +// This is copy-pasta from RCTModuleData. +- (void)_lookupModuleMethodsIfNecessary +{ + if (!_bridge && !_moduleMethods) { + _moduleMethods = [NSMutableArray new]; + _moduleMethodsByName = [NSMutableDictionary new]; + + unsigned int methodCount; + Class cls = _componentData.managerClass; + while (cls && cls != [NSObject class] && cls != [NSProxy class]) { + Method *methods = class_copyMethodList(object_getClass(cls), &methodCount); + + for (unsigned int i = 0; i < methodCount; i++) { + Method method = methods[i]; + SEL selector = method_getName(method); + if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) { + IMP imp = method_getImplementation(method); + auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_componentData.managerClass, selector); + id moduleMethod = + [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod moduleClass:_componentData.managerClass]; + [_moduleMethodsByName setValue:moduleMethod forKey:[NSString stringWithUTF8String:moduleMethod.JSMethodName]]; + [_moduleMethods addObject:moduleMethod]; + } } - queue:RCTGetUIManagerQueue()]; + + free(methods); + cls = class_getSuperclass(cls); + } + } } @end diff --git a/android/ReactCommon/react/renderer/components/modal/Android.mk b/android/ReactCommon/react/renderer/components/modal/Android.mk index dd404e8dbda30..cd0a046e73204 100644 --- a/android/ReactCommon/react/renderer/components/modal/Android.mk +++ b/android/ReactCommon/react/renderer/components/modal/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_modal +LOCAL_MODULE := rrc_modal LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_image libreact_render_uimanager libreact_render_imagemanager libreact_render_components_view libreact_render_componentregistry libreact_render_viewmanagers +LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_image libreact_render_uimanager libreact_render_imagemanager librrc_view libreact_render_componentregistry libreact_codegen_rncore libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -37,3 +37,4 @@ $(call import-module,react/renderer/uimanager) $(call import-module,react/renderer/components/image) $(call import-module,react/renderer/components/view) $(call import-module,yogajni) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/components/modal/BUCK b/android/ReactCommon/react/renderer/components/modal/BUCK index f2865199207d7..10c9e229be2d0 100644 --- a/android/ReactCommon/react/renderer/components/modal/BUCK +++ b/android/ReactCommon/react/renderer/components/modal/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "YOGA_CXX_TARGET", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -31,11 +31,8 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/modal", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", + fbandroid_deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbandroid_exported_headers = subdir_glob( [ @@ -75,6 +72,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/components/image:image"), diff --git a/android/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h b/android/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h index e821c911d3887..c1130183ad142 100644 --- a/android/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h @@ -23,13 +23,10 @@ class ModalHostViewComponentDescriptor final public: using ConcreteComponentDescriptor::ConcreteComponentDescriptor; - void adopt(UnsharedShadowNode shadowNode) const override { - assert(std::dynamic_pointer_cast(shadowNode)); + void adopt(ShadowNode::Unshared const &shadowNode) const override { auto modalShadowNode = std::static_pointer_cast(shadowNode); - assert( - std::dynamic_pointer_cast(modalShadowNode)); auto layoutableShadowNode = std::static_pointer_cast(modalShadowNode); diff --git a/android/ReactCommon/react/renderer/components/modal/ModalHostViewState.h b/android/ReactCommon/react/renderer/components/modal/ModalHostViewState.h index 51e3677ff5459..b0921864ba779 100644 --- a/android/ReactCommon/react/renderer/components/modal/ModalHostViewState.h +++ b/android/ReactCommon/react/renderer/components/modal/ModalHostViewState.h @@ -13,6 +13,8 @@ #ifdef ANDROID #include +#include +#include #endif namespace facebook { @@ -32,14 +34,19 @@ class ModalHostViewState final { ModalHostViewState( ModalHostViewState const &previousState, folly::dynamic data) - : screenSize(Size{(Float)data["screenWidth"].getDouble(), - (Float)data["screenHeight"].getDouble()}){}; + : screenSize(Size{ + (Float)data["screenWidth"].getDouble(), + (Float)data["screenHeight"].getDouble()}){}; #endif const Size screenSize{}; #ifdef ANDROID folly::dynamic getDynamic() const; + MapBuffer getMapBuffer() const { + return MapBufferBuilder::EMPTY(); + }; + #endif #pragma mark - Getters diff --git a/android/ReactCommon/react/renderer/components/picker/Android.mk b/android/ReactCommon/react/renderer/components/picker/Android.mk deleted file mode 100644 index be55965cece46..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/Android.mk +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := react_render_components_picker - -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/androidpicker/react/renderer/components/androidpicker/*.cpp) - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/androidpicker/react/renderer/components/androidpicker/ -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/androidpicker/ - -LOCAL_CFLAGS := \ - -DLOG_TAG=\"Fabric\" - -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_uimanager libreact_render_components_view libreact_render_componentregistry libreact_render_viewmanagers - -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,glog) -$(call import-module,folly) -$(call import-module,fbgloginit) -$(call import-module,react/renderer/core) -$(call import-module,react/renderer/componentregistry) -$(call import-module,react/renderer/debug) -$(call import-module,react/renderer/graphics) -$(call import-module,react/renderer/uimanager) -$(call import-module,react/renderer/components/view) -$(call import-module,yogajni) diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.cpp b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.cpp deleted file mode 100644 index 983a7f5dfffaf..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AndroidDialogPickerEventEmitter.h" - -namespace facebook { -namespace react { - -void AndroidDialogPickerEventEmitter::onSelect( - AndroidDialogPickerOnSelectStruct event) const { - dispatchEvent("select", [event = std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "position", event.position); - return payload; - }); -} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.h b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.h deleted file mode 100644 index 37781f4b5a8e5..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerEventEmitter.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace facebook { -namespace react { - -struct AndroidDialogPickerOnSelectStruct { - int position; -}; - -class AndroidDialogPickerEventEmitter : public ViewEventEmitter { - public: - using ViewEventEmitter::ViewEventEmitter; - - void onSelect(AndroidDialogPickerOnSelectStruct value) const; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.cpp b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.cpp deleted file mode 100644 index c44238843b03e..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AndroidDialogPickerProps.h" - -#include -#include - -namespace facebook { -namespace react { - -AndroidDialogPickerProps::AndroidDialogPickerProps( - const AndroidDialogPickerProps &sourceProps, - const RawProps &rawProps) - : ViewProps(sourceProps, rawProps), - color(convertRawProp(rawProps, "color", sourceProps.color, {})), - enabled(convertRawProp(rawProps, "enabled", sourceProps.enabled, {true})), - items(convertRawProp(rawProps, "items", sourceProps.items, {})), - prompt(convertRawProp(rawProps, "prompt", sourceProps.prompt, {""})), - selected( - convertRawProp(rawProps, "selected", sourceProps.selected, {0})) {} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.h b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.h deleted file mode 100644 index e4868a7f67633..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerProps.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace react { - -struct AndroidDialogPickerItemsStruct { - std::string label; - int color; -}; - -static inline void fromRawValue( - const RawValue &value, - AndroidDialogPickerItemsStruct &result) { - auto map = (better::map)value; - - auto label = map.find("label"); - if (label != map.end()) { - fromRawValue(label->second, result.label); - } - auto color = map.find("color"); - // C++ props are not used on Android at the moment, so we can leave - // result.color uninitialized if the JS prop has a null value. TODO: revisit - // this once we start using C++ props on Android. - if (color != map.end() && color->second.hasValue()) { - fromRawValue(color->second, result.color); - } -} - -static inline std::string toString( - const AndroidDialogPickerItemsStruct &value) { - return "[Object AndroidDialogPickerItemsStruct]"; -} - -static inline void fromRawValue( - const RawValue &value, - std::vector &result) { - auto items = (std::vector)value; - for (const auto &item : items) { - AndroidDialogPickerItemsStruct newItem; - fromRawValue(item, newItem); - result.emplace_back(newItem); - } -} - -class AndroidDialogPickerProps final : public ViewProps { - public: - AndroidDialogPickerProps() = default; - - AndroidDialogPickerProps( - const AndroidDialogPickerProps &sourceProps, - const RawProps &rawProps); - -#pragma mark - Props - - const SharedColor color{}; - const bool enabled{true}; - const std::vector items{}; - const std::string prompt{""}; - const int selected{0}; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.cpp b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.cpp deleted file mode 100644 index e101c71b02291..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AndroidDialogPickerShadowNode.h" - -namespace facebook { -namespace react { - -extern const char AndroidDialogPickerComponentName[] = "AndroidDialogPicker"; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.h b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.h deleted file mode 100644 index e819b923e80a6..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerShadowNode.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include "AndroidDialogPickerEventEmitter.h" -#include "AndroidDialogPickerProps.h" - -#include - -namespace facebook { -namespace react { - -extern const char AndroidDialogPickerComponentName[]; - -/* - * `ShadowNode` for component. - */ -using AndroidDialogPickerShadowNode = ConcreteViewShadowNode< - AndroidDialogPickerComponentName, - AndroidDialogPickerProps, - AndroidDialogPickerEventEmitter>; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.cpp b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.cpp deleted file mode 100644 index df52918bac4ef..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AndroidDropdownPickerEventEmitter.h" - -namespace facebook { -namespace react { - -void AndroidDropdownPickerEventEmitter::onSelect( - AndroidDropdownPickerOnSelectStruct event) const { - dispatchEvent("select", [event = std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "position", event.position); - return payload; - }); -} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.h b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.h deleted file mode 100644 index 356203b1c6edf..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerEventEmitter.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace facebook { -namespace react { - -struct AndroidDropdownPickerOnSelectStruct { - int position; -}; - -class AndroidDropdownPickerEventEmitter : public ViewEventEmitter { - public: - using ViewEventEmitter::ViewEventEmitter; - - void onSelect(AndroidDropdownPickerOnSelectStruct value) const; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.cpp b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.cpp deleted file mode 100644 index b305b02421069..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AndroidDropdownPickerProps.h" - -#include -#include - -namespace facebook { -namespace react { - -AndroidDropdownPickerProps::AndroidDropdownPickerProps( - const AndroidDropdownPickerProps &sourceProps, - const RawProps &rawProps) - : ViewProps(sourceProps, rawProps), - color(convertRawProp(rawProps, "color", sourceProps.color, {})), - enabled(convertRawProp(rawProps, "enabled", sourceProps.enabled, {true})), - items(convertRawProp(rawProps, "items", sourceProps.items, {})), - prompt(convertRawProp(rawProps, "prompt", sourceProps.prompt, {""})), - selected( - convertRawProp(rawProps, "selected", sourceProps.selected, {0})) {} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.h b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.h deleted file mode 100644 index 3e66603c4ee7b..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerProps.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace react { - -struct AndroidDropdownPickerItemsStruct { - std::string label; - int color; -}; - -static inline void fromRawValue( - const RawValue &value, - AndroidDropdownPickerItemsStruct &result) { - auto map = (better::map)value; - - auto label = map.find("label"); - if (label != map.end()) { - fromRawValue(label->second, result.label); - } - auto color = map.find("color"); - // C++ props are not used on Android at the moment, so we can leave - // result.color uninitialized if the JS prop has a null value. TODO: revisit - // this once we start using C++ props on Android. - if (color != map.end() && color->second.hasValue()) { - fromRawValue(color->second, result.color); - } -} - -static inline std::string toString( - const AndroidDropdownPickerItemsStruct &value) { - return "[Object AndroidDropdownPickerItemsStruct]"; -} - -static inline void fromRawValue( - const RawValue &value, - std::vector &result) { - auto items = (std::vector)value; - for (const auto &item : items) { - AndroidDropdownPickerItemsStruct newItem; - fromRawValue(item, newItem); - result.emplace_back(newItem); - } -} - -class AndroidDropdownPickerProps final : public ViewProps { - public: - AndroidDropdownPickerProps() = default; - - AndroidDropdownPickerProps( - const AndroidDropdownPickerProps &sourceProps, - const RawProps &rawProps); - -#pragma mark - Props - - const SharedColor color{}; - const bool enabled{true}; - const std::vector items{}; - const std::string prompt{""}; - const int selected{0}; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.h b/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.h deleted file mode 100644 index 5c4ac182798c0..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include "AndroidDropdownPickerEventEmitter.h" -#include "AndroidDropdownPickerProps.h" - -#include - -namespace facebook { -namespace react { - -extern const char AndroidDropdownPickerComponentName[]; - -/* - * `ShadowNode` for component. - */ -using AndroidDropdownPickerShadowNode = ConcreteViewShadowNode< - AndroidDropdownPickerComponentName, - AndroidDropdownPickerProps, - AndroidDropdownPickerEventEmitter>; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h b/android/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h deleted file mode 100644 index d134689e78462..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -/* - * Descriptor for component. - */ -namespace facebook { -namespace react { - -class PickerComponentDescriptor final - : public ConcreteComponentDescriptor { - public: - using ConcreteComponentDescriptor::ConcreteComponentDescriptor; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.cpp b/android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.cpp deleted file mode 100644 index 9e60896606353..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "PickerEventEmitter.h" - -namespace facebook { -namespace react { - -void PickerEventEmitter::onChange(PickerIOSChangeEvent event) const { - dispatchEvent("change", [event = std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "newValue", event.newValue); - payload.setProperty(runtime, "newIndex", event.newIndex); - return payload; - }); -} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp b/android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp deleted file mode 100644 index b3a06ad002978..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "PickerProps.h" - -#include -#include -#include - -namespace facebook { -namespace react { - -PickerProps::PickerProps( - PickerProps const &sourceProps, - RawProps const &rawProps) - : ViewProps(sourceProps, rawProps), - BaseTextProps(sourceProps, rawProps), - items(convertRawProp(rawProps, "items", sourceProps.items, {})), - selectedIndex(convertRawProp( - rawProps, - "selectedIndex", - sourceProps.selectedIndex, - {0})), - testID(convertRawProp(rawProps, "testID", sourceProps.testID, {})), - accessibilityLabel(convertRawProp( - rawProps, - "accessibilityLabel", - sourceProps.accessibilityLabel, - {})){ - - }; - -TextAttributes PickerProps::getEffectiveTextAttributes() const { - auto result = TextAttributes::defaultTextAttributes(); - // Default is left aligned, but Picker wants default to be center aligned. - result.alignment = TextAlignment::Center; - result.apply(textAttributes); - return result; -} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h b/android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h deleted file mode 100644 index c87ca2fe4969a..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace facebook { -namespace react { - -class PickerProps final : public ViewProps, public BaseTextProps { - public: - PickerProps() = default; - PickerProps(PickerProps const &sourceProps, RawProps const &rawProps); - -#pragma mark - Props - - std::vector items{}; - int selectedIndex{0}; - std::string const testID{}; - std::string const accessibilityLabel{}; - -#pragma mark - Accessors - TextAttributes getEffectiveTextAttributes() const; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h b/android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h deleted file mode 100644 index c2c80bc6ae2a0..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -namespace facebook { -namespace react { - -extern const char PickerComponentName[]; - -/* - * `ShadowNode` for component. - */ -class PickerShadowNode final : public ConcreteViewShadowNode< - PickerComponentName, - PickerProps, - PickerEventEmitter, - PickerState> { - public: - using ConcreteViewShadowNode::ConcreteViewShadowNode; -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/conversions.h b/android/ReactCommon/react/renderer/components/picker/iospicker/conversions.h deleted file mode 100644 index b39f10431b78b..0000000000000 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/conversions.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include - -namespace facebook { -namespace react { - -inline void fromRawValue( - const RawValue &value, - std::vector &items) { - assert(value.hasType>()); - auto array = (std::vector)value; - items.reserve(array.size()); - - for (auto const &val : array) { - bool check = val.hasType>(); - assert(check); - auto map = (better::map)val; - PickerItemsStruct item; - - if (map.find("label") != map.end()) { - assert(map.at("label").hasType()); - item.label = (std::string)map.at("label"); - } - if (map.find("value") != map.end()) { - assert(map.at("value").hasType()); - item.value = (std::string)map.at("value"); - } - if (map.find("textColor") != map.end()) { - assert(map.at("textColor").hasType()); - item.textColor = (int)map.at("textColor"); - } - items.push_back(item); - } -} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/progressbar/Android.mk b/android/ReactCommon/react/renderer/components/progressbar/Android.mk index f17cb34c809c5..b899b97badae1 100644 --- a/android/ReactCommon/react/renderer/components/progressbar/Android.mk +++ b/android/ReactCommon/react/renderer/components/progressbar/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_progressbar +LOCAL_MODULE := rrc_progressbar LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/android/react/renderer/components/progressbar/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/android/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libfbjni libreact_render_viewmanagers libreactnativeutilsjni libreact_render_componentregistry libreact_render_uimanager libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view +LOCAL_SHARED_LIBRARIES := libfbjni libreact_codegen_rncore libreactnativeutilsjni libreact_render_componentregistry libreact_render_uimanager libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_debug include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactCommon/react/renderer/components/progressbar/BUCK b/android/ReactCommon/react/renderer/components/progressbar/BUCK index 8afe40dc616d1..b45bde96a1af2 100644 --- a/android/ReactCommon/react/renderer/components/progressbar/BUCK +++ b/android/ReactCommon/react/renderer/components/progressbar/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", @@ -32,12 +32,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/progressbar", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_tests = [":tests"], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), @@ -85,6 +79,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/components/image:image"), @@ -104,7 +99,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h b/android/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h index 4b3802666a1e2..9e012b91cc56a 100644 --- a/android/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h @@ -27,10 +27,9 @@ class AndroidProgressBarComponentDescriptor final std::make_shared( contextContainer_)) {} - void adopt(UnsharedShadowNode shadowNode) const override { + void adopt(ShadowNode::Unshared const &shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); - assert(std::dynamic_pointer_cast(shadowNode)); auto androidProgressBarShadowNode = std::static_pointer_cast(shadowNode); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h b/android/ReactCommon/react/renderer/components/rncore/ComponentDescriptors.h similarity index 91% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h rename to android/ReactCommon/react/renderer/components/rncore/ComponentDescriptors.h index cc3c195dd3a24..8f89f529b2516 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h +++ b/android/ReactCommon/react/renderer/components/rncore/ComponentDescriptors.h @@ -16,16 +16,17 @@ namespace facebook { namespace react { -using ActivityIndicatorViewComponentDescriptor = ConcreteComponentDescriptor; -using DatePickerComponentDescriptor = ConcreteComponentDescriptor; -using AndroidDrawerLayoutComponentDescriptor = ConcreteComponentDescriptor; -using RCTMaskedViewComponentDescriptor = ConcreteComponentDescriptor; +using AndroidHorizontalScrollContentViewComponentDescriptor = ConcreteComponentDescriptor; using RCTProgressViewComponentDescriptor = ConcreteComponentDescriptor; using AndroidSwipeRefreshLayoutComponentDescriptor = ConcreteComponentDescriptor; using PullToRefreshViewComponentDescriptor = ConcreteComponentDescriptor; +using DatePickerComponentDescriptor = ConcreteComponentDescriptor; +using AndroidDrawerLayoutComponentDescriptor = ConcreteComponentDescriptor; using RCTSegmentedControlComponentDescriptor = ConcreteComponentDescriptor; -using SwitchComponentDescriptor = ConcreteComponentDescriptor; +using RCTMaskedViewComponentDescriptor = ConcreteComponentDescriptor; +using ActivityIndicatorViewComponentDescriptor = ConcreteComponentDescriptor; using UnimplementedNativeViewComponentDescriptor = ConcreteComponentDescriptor; +using SwitchComponentDescriptor = ConcreteComponentDescriptor; } // namespace react } // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/EventEmitters.cpp b/android/ReactCommon/react/renderer/components/rncore/EventEmitters.cpp similarity index 99% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/EventEmitters.cpp rename to android/ReactCommon/react/renderer/components/rncore/EventEmitters.cpp index ee226666fb4fd..33324c77dd20a 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/EventEmitters.cpp +++ b/android/ReactCommon/react/renderer/components/rncore/EventEmitters.cpp @@ -14,41 +14,6 @@ namespace facebook { namespace react { -void DatePickerEventEmitter::onChange(OnChange event) const { - dispatchEvent("change", [event=std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "timestamp", event.timestamp); - return payload; - }); -} -void AndroidDrawerLayoutEventEmitter::onDrawerSlide(OnDrawerSlide event) const { - dispatchEvent("drawerSlide", [event=std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "offset", event.offset); - return payload; - }); -} -void AndroidDrawerLayoutEventEmitter::onDrawerStateChanged(OnDrawerStateChanged event) const { - dispatchEvent("drawerStateChanged", [event=std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "drawerState", event.drawerState); - return payload; - }); -} -void AndroidDrawerLayoutEventEmitter::onDrawerOpen(OnDrawerOpen event) const { - dispatchEvent("drawerOpen", [event=std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - - return payload; - }); -} -void AndroidDrawerLayoutEventEmitter::onDrawerClose(OnDrawerClose event) const { - dispatchEvent("drawerClose", [event=std::move(event)](jsi::Runtime &runtime) { - auto payload = jsi::Object(runtime); - - return payload; - }); -} @@ -67,11 +32,10 @@ void PullToRefreshViewEventEmitter::onRefresh(OnRefresh event) const { }); } -void RCTSegmentedControlEventEmitter::onChange(OnChange event) const { +void DatePickerEventEmitter::onChange(OnChange event) const { dispatchEvent("change", [event=std::move(event)](jsi::Runtime &runtime) { auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "value", event.value); -payload.setProperty(runtime, "selectedSegmentIndex", event.selectedSegmentIndex); + payload.setProperty(runtime, "timestamp", event.timestamp); return payload; }); } @@ -99,13 +63,45 @@ payload.setProperty(runtime, "fromUser", event.fromUser); return payload; }); } -void AndroidSwitchEventEmitter::onChange(OnChange event) const { +void AndroidDrawerLayoutEventEmitter::onDrawerSlide(OnDrawerSlide event) const { + dispatchEvent("drawerSlide", [event=std::move(event)](jsi::Runtime &runtime) { + auto payload = jsi::Object(runtime); + payload.setProperty(runtime, "offset", event.offset); + return payload; + }); +} +void AndroidDrawerLayoutEventEmitter::onDrawerStateChanged(OnDrawerStateChanged event) const { + dispatchEvent("drawerStateChanged", [event=std::move(event)](jsi::Runtime &runtime) { + auto payload = jsi::Object(runtime); + payload.setProperty(runtime, "drawerState", event.drawerState); + return payload; + }); +} +void AndroidDrawerLayoutEventEmitter::onDrawerOpen(OnDrawerOpen event) const { + dispatchEvent("drawerOpen", [event=std::move(event)](jsi::Runtime &runtime) { + auto payload = jsi::Object(runtime); + + return payload; + }); +} +void AndroidDrawerLayoutEventEmitter::onDrawerClose(OnDrawerClose event) const { + dispatchEvent("drawerClose", [event=std::move(event)](jsi::Runtime &runtime) { + auto payload = jsi::Object(runtime); + + return payload; + }); +} +void RCTSegmentedControlEventEmitter::onChange(OnChange event) const { dispatchEvent("change", [event=std::move(event)](jsi::Runtime &runtime) { auto payload = jsi::Object(runtime); payload.setProperty(runtime, "value", event.value); +payload.setProperty(runtime, "selectedSegmentIndex", event.selectedSegmentIndex); return payload; }); } + + + void SwitchEventEmitter::onChange(OnChange event) const { dispatchEvent("change", [event=std::move(event)](jsi::Runtime &runtime) { auto payload = jsi::Object(runtime); @@ -113,8 +109,13 @@ void SwitchEventEmitter::onChange(OnChange event) const { return payload; }); } - - +void AndroidSwitchEventEmitter::onChange(OnChange event) const { + dispatchEvent("change", [event=std::move(event)](jsi::Runtime &runtime) { + auto payload = jsi::Object(runtime); + payload.setProperty(runtime, "value", event.value); + return payload; + }); +} void ModalHostViewEventEmitter::onRequestClose(OnRequestClose event) const { dispatchEvent("requestClose", [event=std::move(event)](jsi::Runtime &runtime) { auto payload = jsi::Object(runtime); diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/EventEmitters.h b/android/ReactCommon/react/renderer/components/rncore/EventEmitters.h similarity index 96% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/EventEmitters.h rename to android/ReactCommon/react/renderer/components/rncore/EventEmitters.h index 16cc009e71573..43893ff71c459 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/EventEmitters.h +++ b/android/ReactCommon/react/renderer/components/rncore/EventEmitters.h @@ -14,7 +14,7 @@ namespace facebook { namespace react { -class ActivityIndicatorViewEventEmitter : public ViewEventEmitter { +class SafeAreaViewEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; @@ -22,45 +22,7 @@ class ActivityIndicatorViewEventEmitter : public ViewEventEmitter { }; -class DatePickerEventEmitter : public ViewEventEmitter { - public: - using ViewEventEmitter::ViewEventEmitter; - - struct OnChange { - Float timestamp; - }; - - void onChange(OnChange value) const; -}; -class AndroidDrawerLayoutEventEmitter : public ViewEventEmitter { - public: - using ViewEventEmitter::ViewEventEmitter; - - struct OnDrawerSlide { - Float offset; - }; - - struct OnDrawerStateChanged { - int drawerState; - }; - - struct OnDrawerOpen { - - }; - - struct OnDrawerClose { - - }; - - void onDrawerSlide(OnDrawerSlide value) const; - - void onDrawerStateChanged(OnDrawerStateChanged value) const; - - void onDrawerOpen(OnDrawerOpen value) const; - - void onDrawerClose(OnDrawerClose value) const; -}; -class RCTMaskedViewEventEmitter : public ViewEventEmitter { +class AndroidHorizontalScrollContentViewEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; @@ -68,7 +30,7 @@ class RCTMaskedViewEventEmitter : public ViewEventEmitter { }; -class AndroidProgressBarEventEmitter : public ViewEventEmitter { +class RCTProgressViewEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; @@ -76,7 +38,7 @@ class AndroidProgressBarEventEmitter : public ViewEventEmitter { }; -class RCTProgressViewEventEmitter : public ViewEventEmitter { +class AndroidProgressBarEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; @@ -104,7 +66,7 @@ class PullToRefreshViewEventEmitter : public ViewEventEmitter { void onRefresh(OnRefresh value) const; }; -class SafeAreaViewEventEmitter : public ViewEventEmitter { +class InputAccessoryEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; @@ -112,13 +74,12 @@ class SafeAreaViewEventEmitter : public ViewEventEmitter { }; -class RCTSegmentedControlEventEmitter : public ViewEventEmitter { +class DatePickerEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; struct OnChange { - int value; - int selectedSegmentIndex; + Float timestamp; }; void onChange(OnChange value) const; @@ -148,27 +109,54 @@ class SliderEventEmitter : public ViewEventEmitter { void onSlidingComplete(OnSlidingComplete value) const; }; -class AndroidSwitchEventEmitter : public ViewEventEmitter { +class AndroidDrawerLayoutEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; - struct OnChange { - bool value; + struct OnDrawerSlide { + Float offset; }; - void onChange(OnChange value) const; + struct OnDrawerStateChanged { + int drawerState; + }; + + struct OnDrawerOpen { + + }; + + struct OnDrawerClose { + + }; + + void onDrawerSlide(OnDrawerSlide value) const; + + void onDrawerStateChanged(OnDrawerStateChanged value) const; + + void onDrawerOpen(OnDrawerOpen value) const; + + void onDrawerClose(OnDrawerClose value) const; }; -class SwitchEventEmitter : public ViewEventEmitter { +class RCTSegmentedControlEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; struct OnChange { - bool value; + int value; + int selectedSegmentIndex; }; void onChange(OnChange value) const; }; -class InputAccessoryEventEmitter : public ViewEventEmitter { +class RCTMaskedViewEventEmitter : public ViewEventEmitter { + public: + using ViewEventEmitter::ViewEventEmitter; + + + + +}; +class ActivityIndicatorViewEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; @@ -183,6 +171,26 @@ class UnimplementedNativeViewEventEmitter : public ViewEventEmitter { +}; +class SwitchEventEmitter : public ViewEventEmitter { + public: + using ViewEventEmitter::ViewEventEmitter; + + struct OnChange { + bool value; + }; + + void onChange(OnChange value) const; +}; +class AndroidSwitchEventEmitter : public ViewEventEmitter { + public: + using ViewEventEmitter::ViewEventEmitter; + + struct OnChange { + bool value; + }; + + void onChange(OnChange value) const; }; class ModalHostViewEventEmitter : public ViewEventEmitter { public: diff --git a/android/ReactCommon/react/renderer/components/rncore/Props.cpp b/android/ReactCommon/react/renderer/components/rncore/Props.cpp new file mode 100644 index 0000000000000..477b417eb6096 --- /dev/null +++ b/android/ReactCommon/react/renderer/components/rncore/Props.cpp @@ -0,0 +1,218 @@ + +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by codegen project: GeneratePropsCpp.js + */ + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +SafeAreaViewProps::SafeAreaViewProps( + const PropsParserContext &context, + const SafeAreaViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + emulateUnlessSupported(convertRawProp(context, rawProps, "emulateUnlessSupported", sourceProps.emulateUnlessSupported, {false})) + {} +AndroidHorizontalScrollContentViewProps::AndroidHorizontalScrollContentViewProps( + const PropsParserContext &context, + const AndroidHorizontalScrollContentViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps) + + + {} +RCTProgressViewProps::RCTProgressViewProps( + const PropsParserContext &context, + const RCTProgressViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + progressViewStyle(convertRawProp(context, rawProps, "progressViewStyle", sourceProps.progressViewStyle, {RCTProgressViewProgressViewStyle::Default})), + progress(convertRawProp(context, rawProps, "progress", sourceProps.progress, {0.0})), + progressTintColor(convertRawProp(context, rawProps, "progressTintColor", sourceProps.progressTintColor, {})), + trackTintColor(convertRawProp(context, rawProps, "trackTintColor", sourceProps.trackTintColor, {})), + progressImage(convertRawProp(context, rawProps, "progressImage", sourceProps.progressImage, {})), + trackImage(convertRawProp(context, rawProps, "trackImage", sourceProps.trackImage, {})) + {} +AndroidProgressBarProps::AndroidProgressBarProps( + const PropsParserContext &context, + const AndroidProgressBarProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + styleAttr(convertRawProp(context, rawProps, "styleAttr", sourceProps.styleAttr, {})), + typeAttr(convertRawProp(context, rawProps, "typeAttr", sourceProps.typeAttr, {})), + indeterminate(convertRawProp(context, rawProps, "indeterminate", sourceProps.indeterminate, {false})), + progress(convertRawProp(context, rawProps, "progress", sourceProps.progress, {0.0})), + animating(convertRawProp(context, rawProps, "animating", sourceProps.animating, {true})), + color(convertRawProp(context, rawProps, "color", sourceProps.color, {})), + testID(convertRawProp(context, rawProps, "testID", sourceProps.testID, {""})) + {} +AndroidSwipeRefreshLayoutProps::AndroidSwipeRefreshLayoutProps( + const PropsParserContext &context, + const AndroidSwipeRefreshLayoutProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + enabled(convertRawProp(context, rawProps, "enabled", sourceProps.enabled, {true})), + colors(convertRawProp(context, rawProps, "colors", sourceProps.colors, {})), + progressBackgroundColor(convertRawProp(context, rawProps, "progressBackgroundColor", sourceProps.progressBackgroundColor, {})), + size(convertRawProp(context, rawProps, "size", sourceProps.size, {AndroidSwipeRefreshLayoutSize::Default})), + progressViewOffset(convertRawProp(context, rawProps, "progressViewOffset", sourceProps.progressViewOffset, {0.0})), + refreshing(convertRawProp(context, rawProps, "refreshing", sourceProps.refreshing, {false})) + {} +PullToRefreshViewProps::PullToRefreshViewProps( + const PropsParserContext &context, + const PullToRefreshViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + tintColor(convertRawProp(context, rawProps, "tintColor", sourceProps.tintColor, {})), + titleColor(convertRawProp(context, rawProps, "titleColor", sourceProps.titleColor, {})), + title(convertRawProp(context, rawProps, "title", sourceProps.title, {})), + progressViewOffset(convertRawProp(context, rawProps, "progressViewOffset", sourceProps.progressViewOffset, {0.0})), + refreshing(convertRawProp(context, rawProps, "refreshing", sourceProps.refreshing, {false})) + {} +InputAccessoryProps::InputAccessoryProps( + const PropsParserContext &context, + const InputAccessoryProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + backgroundColor(convertRawProp(context, rawProps, "backgroundColor", sourceProps.backgroundColor, {})) + {} +DatePickerProps::DatePickerProps( + const PropsParserContext &context, + const DatePickerProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + date(convertRawProp(context, rawProps, "date", sourceProps.date, {0.0})), + initialDate(convertRawProp(context, rawProps, "initialDate", sourceProps.initialDate, {0.0})), + locale(convertRawProp(context, rawProps, "locale", sourceProps.locale, {})), + maximumDate(convertRawProp(context, rawProps, "maximumDate", sourceProps.maximumDate, {0.0})), + minimumDate(convertRawProp(context, rawProps, "minimumDate", sourceProps.minimumDate, {0.0})), + minuteInterval(convertRawProp(context, rawProps, "minuteInterval", sourceProps.minuteInterval, {DatePickerMinuteInterval::MinuteInterval1})), + mode(convertRawProp(context, rawProps, "mode", sourceProps.mode, {DatePickerMode::Date})), + timeZoneOffsetInMinutes(convertRawProp(context, rawProps, "timeZoneOffsetInMinutes", sourceProps.timeZoneOffsetInMinutes, {0.0})), + pickerStyle(convertRawProp(context, rawProps, "pickerStyle", sourceProps.pickerStyle, {DatePickerPickerStyle::Spinner})) + {} +SliderProps::SliderProps( + const PropsParserContext &context, + const SliderProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + disabled(convertRawProp(context, rawProps, "disabled", sourceProps.disabled, {false})), + enabled(convertRawProp(context, rawProps, "enabled", sourceProps.enabled, {true})), + maximumTrackImage(convertRawProp(context, rawProps, "maximumTrackImage", sourceProps.maximumTrackImage, {})), + maximumTrackTintColor(convertRawProp(context, rawProps, "maximumTrackTintColor", sourceProps.maximumTrackTintColor, {})), + maximumValue(convertRawProp(context, rawProps, "maximumValue", sourceProps.maximumValue, {1.0})), + minimumTrackImage(convertRawProp(context, rawProps, "minimumTrackImage", sourceProps.minimumTrackImage, {})), + minimumTrackTintColor(convertRawProp(context, rawProps, "minimumTrackTintColor", sourceProps.minimumTrackTintColor, {})), + minimumValue(convertRawProp(context, rawProps, "minimumValue", sourceProps.minimumValue, {0.0})), + step(convertRawProp(context, rawProps, "step", sourceProps.step, {0.0})), + testID(convertRawProp(context, rawProps, "testID", sourceProps.testID, {""})), + thumbImage(convertRawProp(context, rawProps, "thumbImage", sourceProps.thumbImage, {})), + thumbTintColor(convertRawProp(context, rawProps, "thumbTintColor", sourceProps.thumbTintColor, {})), + trackImage(convertRawProp(context, rawProps, "trackImage", sourceProps.trackImage, {})), + value(convertRawProp(context, rawProps, "value", sourceProps.value, {0.0})) + {} +AndroidDrawerLayoutProps::AndroidDrawerLayoutProps( + const PropsParserContext &context, + const AndroidDrawerLayoutProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + keyboardDismissMode(convertRawProp(context, rawProps, "keyboardDismissMode", sourceProps.keyboardDismissMode, {AndroidDrawerLayoutKeyboardDismissMode::None})), + drawerBackgroundColor(convertRawProp(context, rawProps, "drawerBackgroundColor", sourceProps.drawerBackgroundColor, {})), + drawerPosition(convertRawProp(context, rawProps, "drawerPosition", sourceProps.drawerPosition, {AndroidDrawerLayoutDrawerPosition::Left})), + drawerWidth(convertRawProp(context, rawProps, "drawerWidth", sourceProps.drawerWidth, {})), + drawerLockMode(convertRawProp(context, rawProps, "drawerLockMode", sourceProps.drawerLockMode, {AndroidDrawerLayoutDrawerLockMode::Unlocked})), + statusBarBackgroundColor(convertRawProp(context, rawProps, "statusBarBackgroundColor", sourceProps.statusBarBackgroundColor, {})) + {} +RCTSegmentedControlProps::RCTSegmentedControlProps( + const PropsParserContext &context, + const RCTSegmentedControlProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + values(convertRawProp(context, rawProps, "values", sourceProps.values, {})), + selectedIndex(convertRawProp(context, rawProps, "selectedIndex", sourceProps.selectedIndex, {0})), + enabled(convertRawProp(context, rawProps, "enabled", sourceProps.enabled, {true})), + tintColor(convertRawProp(context, rawProps, "tintColor", sourceProps.tintColor, {})), + textColor(convertRawProp(context, rawProps, "textColor", sourceProps.textColor, {})), + backgroundColor(convertRawProp(context, rawProps, "backgroundColor", sourceProps.backgroundColor, {})), + momentary(convertRawProp(context, rawProps, "momentary", sourceProps.momentary, {false})) + {} +RCTMaskedViewProps::RCTMaskedViewProps( + const PropsParserContext &context, + const RCTMaskedViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps) + + + {} +ActivityIndicatorViewProps::ActivityIndicatorViewProps( + const PropsParserContext &context, + const ActivityIndicatorViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + hidesWhenStopped(convertRawProp(context, rawProps, "hidesWhenStopped", sourceProps.hidesWhenStopped, {false})), + animating(convertRawProp(context, rawProps, "animating", sourceProps.animating, {false})), + color(convertRawProp(context, rawProps, "color", sourceProps.color, {})), + size(convertRawProp(context, rawProps, "size", sourceProps.size, {ActivityIndicatorViewSize::Small})) + {} +UnimplementedNativeViewProps::UnimplementedNativeViewProps( + const PropsParserContext &context, + const UnimplementedNativeViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + name(convertRawProp(context, rawProps, "name", sourceProps.name, {""})) + {} +SwitchProps::SwitchProps( + const PropsParserContext &context, + const SwitchProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + disabled(convertRawProp(context, rawProps, "disabled", sourceProps.disabled, {false})), + value(convertRawProp(context, rawProps, "value", sourceProps.value, {false})), + tintColor(convertRawProp(context, rawProps, "tintColor", sourceProps.tintColor, {})), + onTintColor(convertRawProp(context, rawProps, "onTintColor", sourceProps.onTintColor, {})), + thumbTintColor(convertRawProp(context, rawProps, "thumbTintColor", sourceProps.thumbTintColor, {})), + thumbColor(convertRawProp(context, rawProps, "thumbColor", sourceProps.thumbColor, {})), + trackColorForFalse(convertRawProp(context, rawProps, "trackColorForFalse", sourceProps.trackColorForFalse, {})), + trackColorForTrue(convertRawProp(context, rawProps, "trackColorForTrue", sourceProps.trackColorForTrue, {})) + {} +AndroidSwitchProps::AndroidSwitchProps( + const PropsParserContext &context, + const AndroidSwitchProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + disabled(convertRawProp(context, rawProps, "disabled", sourceProps.disabled, {false})), + enabled(convertRawProp(context, rawProps, "enabled", sourceProps.enabled, {true})), + thumbColor(convertRawProp(context, rawProps, "thumbColor", sourceProps.thumbColor, {})), + trackColorForFalse(convertRawProp(context, rawProps, "trackColorForFalse", sourceProps.trackColorForFalse, {})), + trackColorForTrue(convertRawProp(context, rawProps, "trackColorForTrue", sourceProps.trackColorForTrue, {})), + value(convertRawProp(context, rawProps, "value", sourceProps.value, {false})), + on(convertRawProp(context, rawProps, "on", sourceProps.on, {false})), + thumbTintColor(convertRawProp(context, rawProps, "thumbTintColor", sourceProps.thumbTintColor, {})), + trackTintColor(convertRawProp(context, rawProps, "trackTintColor", sourceProps.trackTintColor, {})) + {} +ModalHostViewProps::ModalHostViewProps( + const PropsParserContext &context, + const ModalHostViewProps &sourceProps, + const RawProps &rawProps): ViewProps(context, sourceProps, rawProps), + + animationType(convertRawProp(context, rawProps, "animationType", sourceProps.animationType, {ModalHostViewAnimationType::None})), + presentationStyle(convertRawProp(context, rawProps, "presentationStyle", sourceProps.presentationStyle, {ModalHostViewPresentationStyle::FullScreen})), + transparent(convertRawProp(context, rawProps, "transparent", sourceProps.transparent, {false})), + statusBarTranslucent(convertRawProp(context, rawProps, "statusBarTranslucent", sourceProps.statusBarTranslucent, {false})), + hardwareAccelerated(convertRawProp(context, rawProps, "hardwareAccelerated", sourceProps.hardwareAccelerated, {false})), + visible(convertRawProp(context, rawProps, "visible", sourceProps.visible, {false})), + animated(convertRawProp(context, rawProps, "animated", sourceProps.animated, {false})), + supportedOrientations(convertRawProp(context, rawProps, "supportedOrientations", sourceProps.supportedOrientations, {static_cast(ModalHostViewSupportedOrientations::Portrait)})), + identifier(convertRawProp(context, rawProps, "identifier", sourceProps.identifier, {0})) + {} + +} // namespace react +} // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.h b/android/ReactCommon/react/renderer/components/rncore/Props.h similarity index 74% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.h rename to android/ReactCommon/react/renderer/components/rncore/Props.h index b968f7e266817..11a4c8e7bd3fe 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/Props.h +++ b/android/ReactCommon/react/renderer/components/rncore/Props.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -18,38 +19,131 @@ namespace facebook { namespace react { -enum class ActivityIndicatorViewSize { Small, Large }; +class SafeAreaViewProps final : public ViewProps { + public: + SafeAreaViewProps() = default; + SafeAreaViewProps(const PropsParserContext& context, const SafeAreaViewProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + bool emulateUnlessSupported{false}; +}; + +class AndroidHorizontalScrollContentViewProps final : public ViewProps { + public: + AndroidHorizontalScrollContentViewProps() = default; + AndroidHorizontalScrollContentViewProps(const PropsParserContext& context, const AndroidHorizontalScrollContentViewProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + +}; + +enum class RCTProgressViewProgressViewStyle { Default, Bar }; -static inline void fromRawValue(const RawValue &value, ActivityIndicatorViewSize &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, RCTProgressViewProgressViewStyle &result) { auto string = (std::string)value; - if (string == "small") { result = ActivityIndicatorViewSize::Small; return; } - if (string == "large") { result = ActivityIndicatorViewSize::Large; return; } + if (string == "default") { result = RCTProgressViewProgressViewStyle::Default; return; } + if (string == "bar") { result = RCTProgressViewProgressViewStyle::Bar; return; } abort(); } -static inline std::string toString(const ActivityIndicatorViewSize &value) { +static inline std::string toString(const RCTProgressViewProgressViewStyle &value) { switch (value) { - case ActivityIndicatorViewSize::Small: return "small"; - case ActivityIndicatorViewSize::Large: return "large"; + case RCTProgressViewProgressViewStyle::Default: return "default"; + case RCTProgressViewProgressViewStyle::Bar: return "bar"; } } -class ActivityIndicatorViewProps final : public ViewProps { +class RCTProgressViewProps final : public ViewProps { public: - ActivityIndicatorViewProps() = default; - ActivityIndicatorViewProps(const ActivityIndicatorViewProps &sourceProps, const RawProps &rawProps); + RCTProgressViewProps() = default; + RCTProgressViewProps(const PropsParserContext& context, const RCTProgressViewProps &sourceProps, const RawProps &rawProps); #pragma mark - Props - bool hidesWhenStopped{false}; - bool animating{false}; + RCTProgressViewProgressViewStyle progressViewStyle{RCTProgressViewProgressViewStyle::Default}; + Float progress{0.0}; + SharedColor progressTintColor{}; + SharedColor trackTintColor{}; + ImageSource progressImage{}; + ImageSource trackImage{}; +}; + +class AndroidProgressBarProps final : public ViewProps { + public: + AndroidProgressBarProps() = default; + AndroidProgressBarProps(const PropsParserContext& context, const AndroidProgressBarProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + std::string styleAttr{}; + std::string typeAttr{}; + bool indeterminate{false}; + double progress{0.0}; + bool animating{true}; SharedColor color{}; - ActivityIndicatorViewSize size{ActivityIndicatorViewSize::Small}; + std::string testID{""}; +}; + +enum class AndroidSwipeRefreshLayoutSize { Default, Large }; + +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, AndroidSwipeRefreshLayoutSize &result) { + auto string = (std::string)value; + if (string == "default") { result = AndroidSwipeRefreshLayoutSize::Default; return; } + if (string == "large") { result = AndroidSwipeRefreshLayoutSize::Large; return; } + abort(); +} + +static inline std::string toString(const AndroidSwipeRefreshLayoutSize &value) { + switch (value) { + case AndroidSwipeRefreshLayoutSize::Default: return "default"; + case AndroidSwipeRefreshLayoutSize::Large: return "large"; + } +} + +class AndroidSwipeRefreshLayoutProps final : public ViewProps { + public: + AndroidSwipeRefreshLayoutProps() = default; + AndroidSwipeRefreshLayoutProps(const PropsParserContext& context, const AndroidSwipeRefreshLayoutProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + bool enabled{true}; + std::vector colors{}; + SharedColor progressBackgroundColor{}; + AndroidSwipeRefreshLayoutSize size{AndroidSwipeRefreshLayoutSize::Default}; + Float progressViewOffset{0.0}; + bool refreshing{false}; +}; + +class PullToRefreshViewProps final : public ViewProps { + public: + PullToRefreshViewProps() = default; + PullToRefreshViewProps(const PropsParserContext& context, const PullToRefreshViewProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + SharedColor tintColor{}; + SharedColor titleColor{}; + std::string title{}; + Float progressViewOffset{0.0}; + bool refreshing{false}; +}; + +class InputAccessoryProps final : public ViewProps { + public: + InputAccessoryProps() = default; + InputAccessoryProps(const PropsParserContext& context, const InputAccessoryProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + SharedColor backgroundColor{}; }; enum class DatePickerMinuteInterval { MinuteInterval1 = 1, MinuteInterval2 = 2, MinuteInterval3 = 3, MinuteInterval4 = 4, MinuteInterval5 = 5, MinuteInterval6 = 6, MinuteInterval10 = 10, MinuteInterval12 = 12, MinuteInterval15 = 15, MinuteInterval20 = 20, MinuteInterval30 = 30 }; -static inline void fromRawValue(const RawValue &value, DatePickerMinuteInterval &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, DatePickerMinuteInterval &result) { assert(value.hasType()); auto integerValue = (int)value; switch (integerValue) { @@ -107,7 +201,7 @@ static inline std::string toString(const DatePickerMinuteInterval &value) { } enum class DatePickerMode { Date, Time, Datetime }; -static inline void fromRawValue(const RawValue &value, DatePickerMode &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, DatePickerMode &result) { auto string = (std::string)value; if (string == "date") { result = DatePickerMode::Date; return; } if (string == "time") { result = DatePickerMode::Time; return; } @@ -122,11 +216,28 @@ static inline std::string toString(const DatePickerMode &value) { case DatePickerMode::Datetime: return "datetime"; } } +enum class DatePickerPickerStyle { Compact, Spinner, Inline }; + +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, DatePickerPickerStyle &result) { + auto string = (std::string)value; + if (string == "compact") { result = DatePickerPickerStyle::Compact; return; } + if (string == "spinner") { result = DatePickerPickerStyle::Spinner; return; } + if (string == "inline") { result = DatePickerPickerStyle::Inline; return; } + abort(); +} + +static inline std::string toString(const DatePickerPickerStyle &value) { + switch (value) { + case DatePickerPickerStyle::Compact: return "compact"; + case DatePickerPickerStyle::Spinner: return "spinner"; + case DatePickerPickerStyle::Inline: return "inline"; + } +} class DatePickerProps final : public ViewProps { public: DatePickerProps() = default; - DatePickerProps(const DatePickerProps &sourceProps, const RawProps &rawProps); + DatePickerProps(const PropsParserContext& context, const DatePickerProps &sourceProps, const RawProps &rawProps); #pragma mark - Props @@ -138,11 +249,35 @@ class DatePickerProps final : public ViewProps { DatePickerMinuteInterval minuteInterval{DatePickerMinuteInterval::MinuteInterval1}; DatePickerMode mode{DatePickerMode::Date}; Float timeZoneOffsetInMinutes{0.0}; + DatePickerPickerStyle pickerStyle{DatePickerPickerStyle::Spinner}; +}; + +class SliderProps final : public ViewProps { + public: + SliderProps() = default; + SliderProps(const PropsParserContext& context, const SliderProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + bool disabled{false}; + bool enabled{true}; + ImageSource maximumTrackImage{}; + SharedColor maximumTrackTintColor{}; + double maximumValue{1.0}; + ImageSource minimumTrackImage{}; + SharedColor minimumTrackTintColor{}; + double minimumValue{0.0}; + double step{0.0}; + std::string testID{""}; + ImageSource thumbImage{}; + SharedColor thumbTintColor{}; + ImageSource trackImage{}; + double value{0.0}; }; enum class AndroidDrawerLayoutKeyboardDismissMode { None, OnDrag }; -static inline void fromRawValue(const RawValue &value, AndroidDrawerLayoutKeyboardDismissMode &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, AndroidDrawerLayoutKeyboardDismissMode &result) { auto string = (std::string)value; if (string == "none") { result = AndroidDrawerLayoutKeyboardDismissMode::None; return; } if (string == "on-drag") { result = AndroidDrawerLayoutKeyboardDismissMode::OnDrag; return; } @@ -157,7 +292,7 @@ static inline std::string toString(const AndroidDrawerLayoutKeyboardDismissMode } enum class AndroidDrawerLayoutDrawerPosition { Left, Right }; -static inline void fromRawValue(const RawValue &value, AndroidDrawerLayoutDrawerPosition &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, AndroidDrawerLayoutDrawerPosition &result) { auto string = (std::string)value; if (string == "left") { result = AndroidDrawerLayoutDrawerPosition::Left; return; } if (string == "right") { result = AndroidDrawerLayoutDrawerPosition::Right; return; } @@ -172,7 +307,7 @@ static inline std::string toString(const AndroidDrawerLayoutDrawerPosition &valu } enum class AndroidDrawerLayoutDrawerLockMode { Unlocked, LockedClosed, LockedOpen }; -static inline void fromRawValue(const RawValue &value, AndroidDrawerLayoutDrawerLockMode &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, AndroidDrawerLayoutDrawerLockMode &result) { auto string = (std::string)value; if (string == "unlocked") { result = AndroidDrawerLayoutDrawerLockMode::Unlocked; return; } if (string == "locked-closed") { result = AndroidDrawerLayoutDrawerLockMode::LockedClosed; return; } @@ -191,7 +326,7 @@ static inline std::string toString(const AndroidDrawerLayoutDrawerLockMode &valu class AndroidDrawerLayoutProps final : public ViewProps { public: AndroidDrawerLayoutProps() = default; - AndroidDrawerLayoutProps(const AndroidDrawerLayoutProps &sourceProps, const RawProps &rawProps); + AndroidDrawerLayoutProps(const PropsParserContext& context, const AndroidDrawerLayoutProps &sourceProps, const RawProps &rawProps); #pragma mark - Props @@ -203,144 +338,92 @@ class AndroidDrawerLayoutProps final : public ViewProps { SharedColor statusBarBackgroundColor{}; }; -class RCTMaskedViewProps final : public ViewProps { +class RCTSegmentedControlProps final : public ViewProps { public: - RCTMaskedViewProps() = default; - RCTMaskedViewProps(const RCTMaskedViewProps &sourceProps, const RawProps &rawProps); + RCTSegmentedControlProps() = default; + RCTSegmentedControlProps(const PropsParserContext& context, const RCTSegmentedControlProps &sourceProps, const RawProps &rawProps); #pragma mark - Props - + std::vector values{}; + int selectedIndex{0}; + bool enabled{true}; + SharedColor tintColor{}; + SharedColor textColor{}; + SharedColor backgroundColor{}; + bool momentary{false}; }; -class AndroidProgressBarProps final : public ViewProps { +class RCTMaskedViewProps final : public ViewProps { public: - AndroidProgressBarProps() = default; - AndroidProgressBarProps(const AndroidProgressBarProps &sourceProps, const RawProps &rawProps); + RCTMaskedViewProps() = default; + RCTMaskedViewProps(const PropsParserContext& context, const RCTMaskedViewProps &sourceProps, const RawProps &rawProps); #pragma mark - Props - std::string styleAttr{}; - std::string typeAttr{}; - bool indeterminate{false}; - double progress{0.0}; - bool animating{true}; - SharedColor color{}; - std::string testID{""}; + }; -enum class RCTProgressViewProgressViewStyle { Default, Bar }; +enum class ActivityIndicatorViewSize { Small, Large }; -static inline void fromRawValue(const RawValue &value, RCTProgressViewProgressViewStyle &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ActivityIndicatorViewSize &result) { auto string = (std::string)value; - if (string == "default") { result = RCTProgressViewProgressViewStyle::Default; return; } - if (string == "bar") { result = RCTProgressViewProgressViewStyle::Bar; return; } + if (string == "small") { result = ActivityIndicatorViewSize::Small; return; } + if (string == "large") { result = ActivityIndicatorViewSize::Large; return; } abort(); } -static inline std::string toString(const RCTProgressViewProgressViewStyle &value) { +static inline std::string toString(const ActivityIndicatorViewSize &value) { switch (value) { - case RCTProgressViewProgressViewStyle::Default: return "default"; - case RCTProgressViewProgressViewStyle::Bar: return "bar"; + case ActivityIndicatorViewSize::Small: return "small"; + case ActivityIndicatorViewSize::Large: return "large"; } } -class RCTProgressViewProps final : public ViewProps { - public: - RCTProgressViewProps() = default; - RCTProgressViewProps(const RCTProgressViewProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - RCTProgressViewProgressViewStyle progressViewStyle{RCTProgressViewProgressViewStyle::Default}; - Float progress{0.0}; - SharedColor progressTintColor{}; - SharedColor trackTintColor{}; - ImageSource progressImage{}; - ImageSource trackImage{}; -}; - -class AndroidSwipeRefreshLayoutProps final : public ViewProps { - public: - AndroidSwipeRefreshLayoutProps() = default; - AndroidSwipeRefreshLayoutProps(const AndroidSwipeRefreshLayoutProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - bool enabled{true}; - std::vector colors{}; - SharedColor progressBackgroundColor{}; - int size{1}; - Float progressViewOffset{0.0}; - bool refreshing{false}; -}; - -class PullToRefreshViewProps final : public ViewProps { - public: - PullToRefreshViewProps() = default; - PullToRefreshViewProps(const PullToRefreshViewProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - SharedColor tintColor{}; - SharedColor titleColor{}; - std::string title{}; - bool refreshing{false}; -}; - -class SafeAreaViewProps final : public ViewProps { +class ActivityIndicatorViewProps final : public ViewProps { public: - SafeAreaViewProps() = default; - SafeAreaViewProps(const SafeAreaViewProps &sourceProps, const RawProps &rawProps); + ActivityIndicatorViewProps() = default; + ActivityIndicatorViewProps(const PropsParserContext& context, const ActivityIndicatorViewProps &sourceProps, const RawProps &rawProps); #pragma mark - Props - bool emulateUnlessSupported{false}; + bool hidesWhenStopped{false}; + bool animating{false}; + SharedColor color{}; + ActivityIndicatorViewSize size{ActivityIndicatorViewSize::Small}; }; -class RCTSegmentedControlProps final : public ViewProps { +class UnimplementedNativeViewProps final : public ViewProps { public: - RCTSegmentedControlProps() = default; - RCTSegmentedControlProps(const RCTSegmentedControlProps &sourceProps, const RawProps &rawProps); + UnimplementedNativeViewProps() = default; + UnimplementedNativeViewProps(const PropsParserContext& context, const UnimplementedNativeViewProps &sourceProps, const RawProps &rawProps); #pragma mark - Props - std::vector values{}; - int selectedIndex{0}; - bool enabled{true}; - SharedColor tintColor{}; - SharedColor textColor{}; - SharedColor backgroundColor{}; - bool momentary{false}; + std::string name{""}; }; -class SliderProps final : public ViewProps { +class SwitchProps final : public ViewProps { public: - SliderProps() = default; - SliderProps(const SliderProps &sourceProps, const RawProps &rawProps); + SwitchProps() = default; + SwitchProps(const PropsParserContext& context, const SwitchProps &sourceProps, const RawProps &rawProps); #pragma mark - Props bool disabled{false}; - bool enabled{true}; - ImageSource maximumTrackImage{}; - SharedColor maximumTrackTintColor{}; - double maximumValue{1.0}; - ImageSource minimumTrackImage{}; - SharedColor minimumTrackTintColor{}; - double minimumValue{0.0}; - double step{0.0}; - std::string testID{""}; - ImageSource thumbImage{}; + bool value{false}; + SharedColor tintColor{}; + SharedColor onTintColor{}; SharedColor thumbTintColor{}; - ImageSource trackImage{}; - double value{0.0}; + SharedColor thumbColor{}; + SharedColor trackColorForFalse{}; + SharedColor trackColorForTrue{}; }; class AndroidSwitchProps final : public ViewProps { public: AndroidSwitchProps() = default; - AndroidSwitchProps(const AndroidSwitchProps &sourceProps, const RawProps &rawProps); + AndroidSwitchProps(const PropsParserContext& context, const AndroidSwitchProps &sourceProps, const RawProps &rawProps); #pragma mark - Props @@ -355,46 +438,9 @@ class AndroidSwitchProps final : public ViewProps { SharedColor trackTintColor{}; }; -class SwitchProps final : public ViewProps { - public: - SwitchProps() = default; - SwitchProps(const SwitchProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - bool disabled{false}; - bool value{false}; - SharedColor tintColor{}; - SharedColor onTintColor{}; - SharedColor thumbTintColor{}; - SharedColor thumbColor{}; - SharedColor trackColorForFalse{}; - SharedColor trackColorForTrue{}; -}; - -class InputAccessoryProps final : public ViewProps { - public: - InputAccessoryProps() = default; - InputAccessoryProps(const InputAccessoryProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - SharedColor backgroundColor{}; -}; - -class UnimplementedNativeViewProps final : public ViewProps { - public: - UnimplementedNativeViewProps() = default; - UnimplementedNativeViewProps(const UnimplementedNativeViewProps &sourceProps, const RawProps &rawProps); - -#pragma mark - Props - - std::string name{""}; -}; - enum class ModalHostViewAnimationType { None, Slide, Fade }; -static inline void fromRawValue(const RawValue &value, ModalHostViewAnimationType &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ModalHostViewAnimationType &result) { auto string = (std::string)value; if (string == "none") { result = ModalHostViewAnimationType::None; return; } if (string == "slide") { result = ModalHostViewAnimationType::Slide; return; } @@ -411,7 +457,7 @@ static inline std::string toString(const ModalHostViewAnimationType &value) { } enum class ModalHostViewPresentationStyle { FullScreen, PageSheet, FormSheet, OverFullScreen }; -static inline void fromRawValue(const RawValue &value, ModalHostViewPresentationStyle &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ModalHostViewPresentationStyle &result) { auto string = (std::string)value; if (string == "fullScreen") { result = ModalHostViewPresentationStyle::FullScreen; return; } if (string == "pageSheet") { result = ModalHostViewPresentationStyle::PageSheet; return; } @@ -456,7 +502,7 @@ constexpr void operator|=( lhs = lhs | static_cast(rhs); } -static inline void fromRawValue(const RawValue &value, ModalHostViewSupportedOrientationsMask &result) { +static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ModalHostViewSupportedOrientationsMask &result) { auto items = std::vector{value}; for (const auto &item : items) { if (item == "portrait") { @@ -511,7 +557,7 @@ static inline std::string toString(const ModalHostViewSupportedOrientationsMask class ModalHostViewProps final : public ViewProps { public: ModalHostViewProps() = default; - ModalHostViewProps(const ModalHostViewProps &sourceProps, const RawProps &rawProps); + ModalHostViewProps(const PropsParserContext& context, const ModalHostViewProps &sourceProps, const RawProps &rawProps); #pragma mark - Props @@ -520,6 +566,7 @@ class ModalHostViewProps final : public ViewProps { bool transparent{false}; bool statusBarTranslucent{false}; bool hardwareAccelerated{false}; + bool visible{false}; bool animated{false}; ModalHostViewSupportedOrientationsMask supportedOrientations{static_cast(ModalHostViewSupportedOrientations::Portrait)}; int identifier{0}; diff --git a/android/ReactCommon/react/renderer/components/rncore/RCTComponentViewHelpers.h b/android/ReactCommon/react/renderer/components/rncore/RCTComponentViewHelpers.h new file mode 100644 index 0000000000000..759ed106d7b69 --- /dev/null +++ b/android/ReactCommon/react/renderer/components/rncore/RCTComponentViewHelpers.h @@ -0,0 +1,273 @@ +/** +* Copyright (c) Facebook, Inc. and its affiliates. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +* +* @generated by codegen project: GenerateComponentHObjCpp.js +*/ + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol RCTSafeAreaViewViewProtocol + +@end + +@protocol RCTAndroidHorizontalScrollContentViewViewProtocol + +@end + +@protocol RCTRCTProgressViewViewProtocol + +@end + +@protocol RCTAndroidProgressBarViewProtocol + +@end + +@protocol RCTAndroidSwipeRefreshLayoutViewProtocol +- (void)setNativeRefreshing:(BOOL)value; +@end + +RCT_EXTERN inline void RCTAndroidSwipeRefreshLayoutHandleCommand( + id componentView, + NSString const *commandName, + NSArray const *args) +{ + if ([commandName isEqualToString:@"setNativeRefreshing"]) { +#if RCT_DEBUG + if ([args count] != 1) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"AndroidSwipeRefreshLayout", commandName, (int)[args count], 1); + return; + } +#endif + + NSObject *arg0 = args[0]; +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"boolean", @"AndroidSwipeRefreshLayout", commandName, @"1st")) { + return; + } +#endif + BOOL value = [(NSNumber *)arg0 boolValue]; + + [componentView setNativeRefreshing:value]; + return; +} + +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"AndroidSwipeRefreshLayout", commandName); +#endif +} + +@protocol RCTPullToRefreshViewViewProtocol +- (void)setNativeRefreshing:(BOOL)refreshing; +@end + +RCT_EXTERN inline void RCTPullToRefreshViewHandleCommand( + id componentView, + NSString const *commandName, + NSArray const *args) +{ + if ([commandName isEqualToString:@"setNativeRefreshing"]) { +#if RCT_DEBUG + if ([args count] != 1) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"PullToRefreshView", commandName, (int)[args count], 1); + return; + } +#endif + + NSObject *arg0 = args[0]; +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"boolean", @"PullToRefreshView", commandName, @"1st")) { + return; + } +#endif + BOOL refreshing = [(NSNumber *)arg0 boolValue]; + + [componentView setNativeRefreshing:refreshing]; + return; +} + +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"PullToRefreshView", commandName); +#endif +} + +@protocol RCTInputAccessoryViewProtocol + +@end + +@protocol RCTDatePickerViewProtocol +- (void)setNativeDate:(float)date; +@end + +RCT_EXTERN inline void RCTDatePickerHandleCommand( + id componentView, + NSString const *commandName, + NSArray const *args) +{ + if ([commandName isEqualToString:@"setNativeDate"]) { +#if RCT_DEBUG + if ([args count] != 1) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"DatePicker", commandName, (int)[args count], 1); + return; + } +#endif + + NSObject *arg0 = args[0]; +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"float", @"DatePicker", commandName, @"1st")) { + return; + } +#endif + float date = [(NSNumber *)arg0 floatValue]; + + [componentView setNativeDate:date]; + return; +} + +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"DatePicker", commandName); +#endif +} + +@protocol RCTSliderViewProtocol + +@end + +@protocol RCTAndroidDrawerLayoutViewProtocol +- (void)openDrawer; +- (void)closeDrawer; +@end + +RCT_EXTERN inline void RCTAndroidDrawerLayoutHandleCommand( + id componentView, + NSString const *commandName, + NSArray const *args) +{ + if ([commandName isEqualToString:@"openDrawer"]) { +#if RCT_DEBUG + if ([args count] != 0) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"AndroidDrawerLayout", commandName, (int)[args count], 0); + return; + } +#endif + + + + [componentView openDrawer]; + return; +} + +if ([commandName isEqualToString:@"closeDrawer"]) { +#if RCT_DEBUG + if ([args count] != 0) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"AndroidDrawerLayout", commandName, (int)[args count], 0); + return; + } +#endif + + + + [componentView closeDrawer]; + return; +} + +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"AndroidDrawerLayout", commandName); +#endif +} + +@protocol RCTRCTSegmentedControlViewProtocol + +@end + +@protocol RCTRCTMaskedViewViewProtocol + +@end + +@protocol RCTActivityIndicatorViewViewProtocol + +@end + +@protocol RCTUnimplementedNativeViewViewProtocol + +@end + +@protocol RCTSwitchViewProtocol +- (void)setValue:(BOOL)value; +@end + +RCT_EXTERN inline void RCTSwitchHandleCommand( + id componentView, + NSString const *commandName, + NSArray const *args) +{ + if ([commandName isEqualToString:@"setValue"]) { +#if RCT_DEBUG + if ([args count] != 1) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"Switch", commandName, (int)[args count], 1); + return; + } +#endif + + NSObject *arg0 = args[0]; +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"boolean", @"Switch", commandName, @"1st")) { + return; + } +#endif + BOOL value = [(NSNumber *)arg0 boolValue]; + + [componentView setValue:value]; + return; +} + +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"Switch", commandName); +#endif +} + +@protocol RCTAndroidSwitchViewProtocol +- (void)setNativeValue:(BOOL)value; +@end + +RCT_EXTERN inline void RCTAndroidSwitchHandleCommand( + id componentView, + NSString const *commandName, + NSArray const *args) +{ + if ([commandName isEqualToString:@"setNativeValue"]) { +#if RCT_DEBUG + if ([args count] != 1) { + RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"AndroidSwitch", commandName, (int)[args count], 1); + return; + } +#endif + + NSObject *arg0 = args[0]; +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"boolean", @"AndroidSwitch", commandName, @"1st")) { + return; + } +#endif + BOOL value = [(NSNumber *)arg0 boolValue]; + + [componentView setNativeValue:value]; + return; +} + +#if RCT_DEBUG + RCTLogError(@"%@ received command %@, which is not a supported command.", @"AndroidSwitch", commandName); +#endif +} + +@protocol RCTModalHostViewViewProtocol + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp b/android/ReactCommon/react/renderer/components/rncore/ShadowNodes.cpp similarity index 91% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp rename to android/ReactCommon/react/renderer/components/rncore/ShadowNodes.cpp index befdb19c30df3..15208991ba7b6 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp +++ b/android/ReactCommon/react/renderer/components/rncore/ShadowNodes.cpp @@ -13,16 +13,17 @@ namespace facebook { namespace react { -extern const char ActivityIndicatorViewComponentName[] = "ActivityIndicatorView"; -extern const char DatePickerComponentName[] = "DatePicker"; -extern const char AndroidDrawerLayoutComponentName[] = "AndroidDrawerLayout"; -extern const char RCTMaskedViewComponentName[] = "RCTMaskedView"; +extern const char AndroidHorizontalScrollContentViewComponentName[] = "AndroidHorizontalScrollContentView"; extern const char RCTProgressViewComponentName[] = "RCTProgressView"; extern const char AndroidSwipeRefreshLayoutComponentName[] = "AndroidSwipeRefreshLayout"; extern const char PullToRefreshViewComponentName[] = "PullToRefreshView"; +extern const char DatePickerComponentName[] = "DatePicker"; +extern const char AndroidDrawerLayoutComponentName[] = "AndroidDrawerLayout"; extern const char RCTSegmentedControlComponentName[] = "RCTSegmentedControl"; -extern const char SwitchComponentName[] = "Switch"; +extern const char RCTMaskedViewComponentName[] = "RCTMaskedView"; +extern const char ActivityIndicatorViewComponentName[] = "ActivityIndicatorView"; extern const char UnimplementedNativeViewComponentName[] = "UnimplementedNativeView"; +extern const char SwitchComponentName[] = "Switch"; } // namespace react } // namespace facebook diff --git a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h b/android/ReactCommon/react/renderer/components/rncore/ShadowNodes.h similarity index 90% rename from android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h rename to android/ReactCommon/react/renderer/components/rncore/ShadowNodes.h index f8a84f5836b01..fe1f213a91fa5 100644 --- a/android/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h +++ b/android/ReactCommon/react/renderer/components/rncore/ShadowNodes.h @@ -17,43 +17,14 @@ namespace facebook { namespace react { -extern const char ActivityIndicatorViewComponentName[]; - -/* - * `ShadowNode` for component. - */ -using ActivityIndicatorViewShadowNode = ConcreteViewShadowNode< - ActivityIndicatorViewComponentName, - ActivityIndicatorViewProps>; - -extern const char DatePickerComponentName[]; - -/* - * `ShadowNode` for component. - */ -using DatePickerShadowNode = ConcreteViewShadowNode< - DatePickerComponentName, - DatePickerProps, -DatePickerEventEmitter>; - -extern const char AndroidDrawerLayoutComponentName[]; - -/* - * `ShadowNode` for component. - */ -using AndroidDrawerLayoutShadowNode = ConcreteViewShadowNode< - AndroidDrawerLayoutComponentName, - AndroidDrawerLayoutProps, -AndroidDrawerLayoutEventEmitter>; - -extern const char RCTMaskedViewComponentName[]; +extern const char AndroidHorizontalScrollContentViewComponentName[]; /* - * `ShadowNode` for component. + * `ShadowNode` for component. */ -using RCTMaskedViewShadowNode = ConcreteViewShadowNode< - RCTMaskedViewComponentName, - RCTMaskedViewProps>; +using AndroidHorizontalScrollContentViewShadowNode = ConcreteViewShadowNode< + AndroidHorizontalScrollContentViewComponentName, + AndroidHorizontalScrollContentViewProps>; extern const char RCTProgressViewComponentName[]; @@ -84,6 +55,26 @@ using PullToRefreshViewShadowNode = ConcreteViewShadowNode< PullToRefreshViewProps, PullToRefreshViewEventEmitter>; +extern const char DatePickerComponentName[]; + +/* + * `ShadowNode` for component. + */ +using DatePickerShadowNode = ConcreteViewShadowNode< + DatePickerComponentName, + DatePickerProps, +DatePickerEventEmitter>; + +extern const char AndroidDrawerLayoutComponentName[]; + +/* + * `ShadowNode` for component. + */ +using AndroidDrawerLayoutShadowNode = ConcreteViewShadowNode< + AndroidDrawerLayoutComponentName, + AndroidDrawerLayoutProps, +AndroidDrawerLayoutEventEmitter>; + extern const char RCTSegmentedControlComponentName[]; /* @@ -94,15 +85,23 @@ using RCTSegmentedControlShadowNode = ConcreteViewShadowNode< RCTSegmentedControlProps, RCTSegmentedControlEventEmitter>; -extern const char SwitchComponentName[]; +extern const char RCTMaskedViewComponentName[]; /* - * `ShadowNode` for component. + * `ShadowNode` for component. */ -using SwitchShadowNode = ConcreteViewShadowNode< - SwitchComponentName, - SwitchProps, -SwitchEventEmitter>; +using RCTMaskedViewShadowNode = ConcreteViewShadowNode< + RCTMaskedViewComponentName, + RCTMaskedViewProps>; + +extern const char ActivityIndicatorViewComponentName[]; + +/* + * `ShadowNode` for component. + */ +using ActivityIndicatorViewShadowNode = ConcreteViewShadowNode< + ActivityIndicatorViewComponentName, + ActivityIndicatorViewProps>; extern const char UnimplementedNativeViewComponentName[]; @@ -113,5 +112,15 @@ using UnimplementedNativeViewShadowNode = ConcreteViewShadowNode< UnimplementedNativeViewComponentName, UnimplementedNativeViewProps>; +extern const char SwitchComponentName[]; + +/* + * `ShadowNode` for component. + */ +using SwitchShadowNode = ConcreteViewShadowNode< + SwitchComponentName, + SwitchProps, +SwitchEventEmitter>; + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/root/Android.mk b/android/ReactCommon/react/renderer/components/root/Android.mk index b100af0f55407..8149fe3f02fde 100644 --- a/android/ReactCommon/react/renderer/components/root/Android.mk +++ b/android/ReactCommon/react/renderer/components/root/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_root +LOCAL_MODULE := rrc_root LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view +LOCAL_SHARED_LIBRARIES := libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_debug include $(BUILD_SHARED_LIBRARY) @@ -32,4 +32,5 @@ $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) +$(call import-module,react/debug) $(call import-module,yogajni) diff --git a/android/ReactCommon/react/renderer/components/root/BUCK b/android/ReactCommon/react/renderer/components/root/BUCK index 551366f61d537..59cd4b32a0462 100644 --- a/android/ReactCommon/react/renderer/components/root/BUCK +++ b/android/ReactCommon/react/renderer/components/root/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -29,12 +29,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/root", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), labels = ["supermodule:xplat/default/public.react_native.infra"], @@ -67,7 +61,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], @@ -76,5 +70,6 @@ fb_xplat_cxx_test( ":root", "//xplat/folly:molly", "//xplat/third-party/gmock:gtest", + react_native_xplat_target("react/renderer/element:element"), ], ) diff --git a/android/ReactCommon/react/renderer/components/root/RootProps.cpp b/android/ReactCommon/react/renderer/components/root/RootProps.cpp index beb6af92cbf2d..3e14c8a73ad07 100644 --- a/android/ReactCommon/react/renderer/components/root/RootProps.cpp +++ b/android/ReactCommon/react/renderer/components/root/RootProps.cpp @@ -13,10 +13,20 @@ namespace facebook { namespace react { -RootProps::RootProps(RootProps const &sourceProps, RawProps const &rawProps) - : ViewProps(sourceProps, rawProps) {} +// Note that a default/empty context may be passed here from RootShadowNode. +// If that's a problem and the context is necesary here, refactor RootShadowNode +// first. +RootProps::RootProps( + const PropsParserContext &context, + RootProps const &sourceProps, + RawProps const &rawProps) + : ViewProps(context, sourceProps, rawProps) {} +// Note that a default/empty context may be passed here from RootShadowNode. +// If that's a problem and the context is necesary here, refactor RootShadowNode +// first. RootProps::RootProps( + const PropsParserContext &context, RootProps const &sourceProps, LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext) diff --git a/android/ReactCommon/react/renderer/components/root/RootProps.h b/android/ReactCommon/react/renderer/components/root/RootProps.h index d3e2ea2dc056a..d28f02837b93d 100644 --- a/android/ReactCommon/react/renderer/components/root/RootProps.h +++ b/android/ReactCommon/react/renderer/components/root/RootProps.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -19,8 +20,12 @@ namespace react { class RootProps final : public ViewProps { public: RootProps() = default; - RootProps(RootProps const &sourceProps, RawProps const &rawProps); RootProps( + const PropsParserContext &context, + RootProps const &sourceProps, + RawProps const &rawProps); + RootProps( + const PropsParserContext &context, RootProps const &sourceProps, LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext); diff --git a/android/ReactCommon/react/renderer/components/root/RootShadowNode.cpp b/android/ReactCommon/react/renderer/components/root/RootShadowNode.cpp index 099be0d0b38c1..5e611de8d6148 100644 --- a/android/ReactCommon/react/renderer/components/root/RootShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/root/RootShadowNode.cpp @@ -39,15 +39,21 @@ Transform RootShadowNode::getTransform() const { } RootShadowNode::Unshared RootShadowNode::clone( + PropsParserContext const &propsParserContext, LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext) const { auto props = std::make_shared( - getConcreteProps(), layoutConstraints, layoutContext); + propsParserContext, getConcreteProps(), layoutConstraints, layoutContext); auto newRootShadowNode = std::make_shared( *this, ShadowNodeFragment{ /* .props = */ props, }); + + if (layoutConstraints != getConcreteProps().layoutConstraints) { + newRootShadowNode->dirtyLayout(); + } + return newRootShadowNode; } diff --git a/android/ReactCommon/react/renderer/components/root/RootShadowNode.h b/android/ReactCommon/react/renderer/components/root/RootShadowNode.h index 3625e67971889..8d88ec913389d 100644 --- a/android/ReactCommon/react/renderer/components/root/RootShadowNode.h +++ b/android/ReactCommon/react/renderer/components/root/RootShadowNode.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -51,6 +52,7 @@ class RootShadowNode final * Clones the node with given `layoutConstraints` and `layoutContext`. */ RootShadowNode::Unshared clone( + PropsParserContext const &propsParserContext, LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext) const; diff --git a/android/ReactCommon/react/renderer/components/root/tests/RootShadowNodeTest.cpp b/android/ReactCommon/react/renderer/components/root/tests/RootShadowNodeTest.cpp index 964411c92b57b..66d67ee817ca8 100644 --- a/android/ReactCommon/react/renderer/components/root/tests/RootShadowNodeTest.cpp +++ b/android/ReactCommon/react/renderer/components/root/tests/RootShadowNodeTest.cpp @@ -5,10 +5,42 @@ * LICENSE file in the root directory of this source tree. */ -#include +#include +#include +#include #include +#include +#include -TEST(RootShadowNodeTest, testSomething) { - // TODO +namespace facebook::react { + +TEST(RootShadowNodeTest, cloneWithLayoutConstraints) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + auto builder = simpleComponentBuilder(); + std::shared_ptr rootShadowNode; + LayoutConstraints defaultLayoutConstraints = {}; + + auto element = + Element().reference(rootShadowNode).tag(1).props([&] { + auto sharedProps = std::make_shared(); + sharedProps->layoutConstraints = defaultLayoutConstraints; + return sharedProps; + }); + + builder.build(element); + + EXPECT_FALSE(rootShadowNode->getIsLayoutClean()); + EXPECT_TRUE(rootShadowNode->layoutIfNeeded()); + EXPECT_TRUE(rootShadowNode->getIsLayoutClean()); + + auto clonedWithDiffentLayoutConstraints = rootShadowNode->clone( + parserContext, LayoutConstraints{{0, 0}, {10, 10}}, {}); + + EXPECT_FALSE(clonedWithDiffentLayoutConstraints->getIsLayoutClean()); + EXPECT_TRUE(clonedWithDiffentLayoutConstraints->layoutIfNeeded()); } + +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/components/safeareaview/BUCK b/android/ReactCommon/react/renderer/components/safeareaview/BUCK index 3c62e99a01a26..fb85f5b0f07bd 100644 --- a/android/ReactCommon/react/renderer/components/safeareaview/BUCK +++ b/android/ReactCommon/react/renderer/components/safeareaview/BUCK @@ -1,9 +1,9 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "APPLE", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -24,23 +24,18 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/safeareaview", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], - platforms = (APPLE), + platforms = APPLE, preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", "-DWITH_FBSYSTRACE=1", ], visibility = ["PUBLIC"], deps = [ + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/core:core"), "//xplat/js/react-native-github:generated_components-rncore", ], diff --git a/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewComponentDescriptor.h b/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewComponentDescriptor.h index 55f0ad4e9e1c9..ad10ca8b125eb 100644 --- a/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewComponentDescriptor.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -19,12 +20,13 @@ namespace react { class SafeAreaViewComponentDescriptor final : public ConcreteComponentDescriptor { using ConcreteComponentDescriptor::ConcreteComponentDescriptor; - void adopt(UnsharedShadowNode shadowNode) const override { - assert(std::dynamic_pointer_cast(shadowNode)); + void adopt(ShadowNode::Unshared const &shadowNode) const override { + react_native_assert( + std::dynamic_pointer_cast(shadowNode)); auto safeAreaViewShadowNode = std::static_pointer_cast(shadowNode); - assert(std::dynamic_pointer_cast( + react_native_assert(std::dynamic_pointer_cast( safeAreaViewShadowNode)); auto layoutableShadowNode = std::static_pointer_cast( diff --git a/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewShadowNode.h b/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewShadowNode.h index 1bd4e47e62ea6..2a27f0ef227c3 100644 --- a/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewShadowNode.h +++ b/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewShadowNode.h @@ -26,6 +26,13 @@ class SafeAreaViewShadowNode final : public ConcreteViewShadowNode< ViewEventEmitter, SafeAreaViewState> { using ConcreteViewShadowNode::ConcreteViewShadowNode; + + public: + static ShadowNodeTraits BaseTraits() { + auto traits = ConcreteViewShadowNode::BaseTraits(); + traits.set(ShadowNodeTraits::Trait::DirtyYogaNode); + return traits; + } }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewState.h b/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewState.h index 6b4c0c008059c..f64b1bb275b6b 100644 --- a/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewState.h +++ b/android/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewState.h @@ -17,9 +17,7 @@ namespace react { */ class SafeAreaViewState final { public: - using Shared = std::shared_ptr; - - EdgeInsets const padding{}; + EdgeInsets padding{}; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/scrollview/Android.mk b/android/ReactCommon/react/renderer/components/scrollview/Android.mk index 71ecb66096941..9b02ae73f0421 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/Android.mk +++ b/android/ReactCommon/react/renderer/components/scrollview/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_scrollview +LOCAL_MODULE := rrc_scrollview LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view +LOCAL_SHARED_LIBRARIES := libjsi libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_debug libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -32,4 +32,6 @@ $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) +$(call import-module,react/debug) $(call import-module,yogajni) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/components/scrollview/BUCK b/android/ReactCommon/react/renderer/components/scrollview/BUCK index 20700db5057ed..d5c0d3f246873 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/BUCK +++ b/android/ReactCommon/react/renderer/components/scrollview/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -32,11 +32,8 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/scrollview", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", + fbandroid_deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), @@ -71,7 +68,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/scrollview/RCTComponentViewHelpers.h b/android/ReactCommon/react/renderer/components/scrollview/RCTComponentViewHelpers.h index ce4303c2c94af..0fab3ae8160e6 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/RCTComponentViewHelpers.h +++ b/android/ReactCommon/react/renderer/components/scrollview/RCTComponentViewHelpers.h @@ -15,6 +15,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)flashScrollIndicators; - (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated; - (void)scrollToEnd:(BOOL)animated; +- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated; @end RCT_EXTERN inline void @@ -90,6 +91,43 @@ RCTScrollViewHandleCommand(id componentView, NSString con [componentView scrollToEnd:animated]; return; } + + if ([commandName isEqualToString:@"zoomToRect"]) { +#if RCT_DEBUG + if ([args count] != 2) { + RCTLogError( + @"%@ command %@ received %d arguments, expected %d.", @"ScrollView", commandName, (int)[args count], 2); + return; + } +#endif + + NSObject *arg0 = args[0]; + +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument( + arg0, [NSDictionary class], @"dictionary", @"ScrollView", commandName, @"1st")) { + return; + } +#endif + + NSDictionary *rectDict = (NSDictionary *)arg0; + NSNumber *x = rectDict[@"x"]; + NSNumber *y = rectDict[@"y"]; + NSNumber *width = rectDict[@"width"]; + NSNumber *height = rectDict[@"height"]; + CGRect rect = CGRectMake(x.doubleValue, y.doubleValue, width.doubleValue, height.doubleValue); + + NSObject *arg1 = args[1]; +#if RCT_DEBUG + if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @"boolean", @"ScrollView", commandName, @"2nd")) { + return; + } +#endif + + BOOL animated = [(NSNumber *)arg1 boolValue]; + [componentView zoomToRect:rect animated:animated]; + return; + } } NS_ASSUME_NONNULL_END diff --git a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp index c030761da7327..5ac1187937b28 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp +++ b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp @@ -17,155 +17,204 @@ namespace facebook { namespace react { ScrollViewProps::ScrollViewProps( + const PropsParserContext &context, ScrollViewProps const &sourceProps, RawProps const &rawProps) - : ViewProps(sourceProps, rawProps), + : ViewProps(context, sourceProps, rawProps), alwaysBounceHorizontal(convertRawProp( + context, rawProps, "alwaysBounceHorizontal", sourceProps.alwaysBounceHorizontal, {})), alwaysBounceVertical(convertRawProp( + context, rawProps, "alwaysBounceVertical", sourceProps.alwaysBounceVertical, {})), - bounces(convertRawProp(rawProps, "bounces", sourceProps.bounces, true)), + bounces(convertRawProp( + context, + rawProps, + "bounces", + sourceProps.bounces, + true)), bouncesZoom(convertRawProp( + context, rawProps, "bouncesZoom", sourceProps.bouncesZoom, true)), canCancelContentTouches(convertRawProp( + context, rawProps, "canCancelContentTouches", sourceProps.canCancelContentTouches, true)), centerContent(convertRawProp( + context, rawProps, "centerContent", sourceProps.centerContent, {})), automaticallyAdjustContentInsets(convertRawProp( + context, rawProps, "automaticallyAdjustContentInsets", sourceProps.automaticallyAdjustContentInsets, {})), + automaticallyAdjustsScrollIndicatorInsets(convertRawProp( + context, + rawProps, + "automaticallyAdjustsScrollIndicatorInsets", + sourceProps.automaticallyAdjustsScrollIndicatorInsets, + true)), decelerationRate(convertRawProp( + context, rawProps, "decelerationRate", sourceProps.decelerationRate, (Float)0.998)), directionalLockEnabled(convertRawProp( + context, rawProps, "directionalLockEnabled", sourceProps.directionalLockEnabled, {})), indicatorStyle(convertRawProp( + context, rawProps, "indicatorStyle", sourceProps.indicatorStyle, {})), keyboardDismissMode(convertRawProp( + context, rawProps, "keyboardDismissMode", sourceProps.keyboardDismissMode, {})), maximumZoomScale(convertRawProp( + context, rawProps, "maximumZoomScale", sourceProps.maximumZoomScale, (Float)1.0)), minimumZoomScale(convertRawProp( + context, rawProps, "minimumZoomScale", sourceProps.minimumZoomScale, (Float)1.0)), scrollEnabled(convertRawProp( + context, rawProps, "scrollEnabled", sourceProps.scrollEnabled, true)), pagingEnabled(convertRawProp( + context, rawProps, "pagingEnabled", sourceProps.pagingEnabled, {})), pinchGestureEnabled(convertRawProp( + context, rawProps, "pinchGestureEnabled", sourceProps.pinchGestureEnabled, true)), scrollsToTop(convertRawProp( + context, rawProps, "scrollsToTop", sourceProps.scrollsToTop, true)), showsHorizontalScrollIndicator(convertRawProp( + context, rawProps, "showsHorizontalScrollIndicator", sourceProps.showsHorizontalScrollIndicator, true)), showsVerticalScrollIndicator(convertRawProp( + context, rawProps, "showsVerticalScrollIndicator", sourceProps.showsVerticalScrollIndicator, true)), scrollEventThrottle(convertRawProp( + context, rawProps, "scrollEventThrottle", sourceProps.scrollEventThrottle, {})), zoomScale(convertRawProp( + context, rawProps, "zoomScale", sourceProps.zoomScale, (Float)1.0)), contentInset(convertRawProp( + context, rawProps, "contentInset", sourceProps.contentInset, {})), contentOffset(convertRawProp( + context, rawProps, "contentOffset", sourceProps.contentOffset, {})), scrollIndicatorInsets(convertRawProp( + context, rawProps, "scrollIndicatorInsets", sourceProps.scrollIndicatorInsets, {})), snapToInterval(convertRawProp( + context, rawProps, "snapToInterval", sourceProps.snapToInterval, {})), snapToAlignment(convertRawProp( + context, rawProps, "snapToAlignment", sourceProps.snapToAlignment, {})), disableIntervalMomentum(convertRawProp( + context, rawProps, "disableIntervalMomentum", sourceProps.disableIntervalMomentum, {})), snapToOffsets(convertRawProp( + context, rawProps, "snapToOffsets", sourceProps.snapToOffsets, {})), - snapToStart( - convertRawProp(rawProps, "snapToStart", sourceProps.snapToStart, {})), - snapToEnd( - convertRawProp(rawProps, "snapToEnd", sourceProps.snapToEnd, {})), + snapToStart(convertRawProp( + context, + rawProps, + "snapToStart", + sourceProps.snapToStart, + true)), + snapToEnd(convertRawProp( + context, + rawProps, + "snapToEnd", + sourceProps.snapToEnd, + true)), contentInsetAdjustmentBehavior(convertRawProp( + context, rawProps, "contentInsetAdjustmentBehavior", sourceProps.contentInsetAdjustmentBehavior, {ContentInsetAdjustmentBehavior::Never})), scrollToOverflowEnabled(convertRawProp( + context, rawProps, "scrollToOverflowEnabled", sourceProps.scrollToOverflowEnabled, @@ -203,6 +252,10 @@ SharedDebugStringConvertibleList ScrollViewProps::getDebugProps() const { "automaticallyAdjustContentInsets", automaticallyAdjustContentInsets, defaultScrollViewProps.automaticallyAdjustContentInsets), + debugStringConvertibleItem( + "automaticallyAdjustsScrollIndicatorInsets", + automaticallyAdjustsScrollIndicatorInsets, + defaultScrollViewProps.automaticallyAdjustsScrollIndicatorInsets), debugStringConvertibleItem( "decelerationRate", decelerationRate, diff --git a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h index 09336eec7569b..2e83cb16a19fd 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h +++ b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h @@ -9,6 +9,7 @@ #include #include +#include namespace facebook { namespace react { @@ -17,7 +18,10 @@ namespace react { class ScrollViewProps final : public ViewProps { public: ScrollViewProps() = default; - ScrollViewProps(ScrollViewProps const &sourceProps, RawProps const &rawProps); + ScrollViewProps( + const PropsParserContext &context, + ScrollViewProps const &sourceProps, + RawProps const &rawProps); #pragma mark - Props @@ -28,12 +32,13 @@ class ScrollViewProps final : public ViewProps { bool canCancelContentTouches{true}; bool centerContent{}; bool automaticallyAdjustContentInsets{}; - Float decelerationRate{0.998}; + bool automaticallyAdjustsScrollIndicatorInsets{true}; + Float decelerationRate{0.998f}; bool directionalLockEnabled{}; ScrollViewIndicatorStyle indicatorStyle{}; ScrollViewKeyboardDismissMode keyboardDismissMode{}; - Float maximumZoomScale{1.0}; - Float minimumZoomScale{1.0}; + Float maximumZoomScale{1.0f}; + Float minimumZoomScale{1.0f}; bool scrollEnabled{true}; bool pagingEnabled{}; bool pinchGestureEnabled{true}; @@ -41,7 +46,7 @@ class ScrollViewProps final : public ViewProps { bool showsHorizontalScrollIndicator{true}; bool showsVerticalScrollIndicator{true}; Float scrollEventThrottle{}; - Float zoomScale{1.0}; + Float zoomScale{1.0f}; EdgeInsets contentInset{}; Point contentOffset{}; EdgeInsets scrollIndicatorInsets{}; diff --git a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp index a421f8c9de0d0..09c860740f322 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp @@ -7,6 +7,7 @@ #include "ScrollViewShadowNode.h" +#include #include namespace facebook { @@ -30,16 +31,36 @@ void ScrollViewShadowNode::updateStateIfNeeded() { } } +void ScrollViewShadowNode::updateScrollContentOffsetIfNeeded() { +#ifndef ANDROID + if (getLayoutMetrics().layoutDirection == LayoutDirection::RightToLeft) { + // Yoga places `contentView` on the right side of `scrollView` when RTL + // layout is enforced. To correct for this, in RTL setting, correct the + // frame's origin. React Native Classic does this as well in + // `RCTScrollContentShadowView.m`. + for (auto layoutableNode : getLayoutableChildNodes()) { + auto layoutMetrics = layoutableNode->getLayoutMetrics(); + if (layoutMetrics.frame.origin.x != 0) { + layoutMetrics.frame.origin.x = 0; + layoutableNode->setLayoutMetrics(layoutMetrics); + } + } + } +#endif +} + #pragma mark - LayoutableShadowNode void ScrollViewShadowNode::layout(LayoutContext layoutContext) { ConcreteViewShadowNode::layout(layoutContext); + updateScrollContentOffsetIfNeeded(); updateStateIfNeeded(); } Point ScrollViewShadowNode::getContentOriginOffset() const { - auto contentOffset = getStateData().contentOffset; - return {-contentOffset.x, -contentOffset.y}; + auto stateData = getStateData(); + auto contentOffset = stateData.contentOffset; + return {-contentOffset.x, -contentOffset.y + stateData.scrollAwayPaddingTop}; } } // namespace react diff --git a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.h b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.h index 19172a9e74be7..f4d4a5f964bbc 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.h +++ b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.h @@ -36,6 +36,7 @@ class ScrollViewShadowNode final : public ConcreteViewShadowNode< private: void updateStateIfNeeded(); + void updateScrollContentOffsetIfNeeded(); }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.cpp b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.cpp index 34f59908e1132..6f368957a6e8f 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.cpp +++ b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.cpp @@ -11,7 +11,7 @@ namespace facebook { namespace react { Size ScrollViewState::getContentSize() const { - return Size{contentBoundingRect.getMaxX(), contentBoundingRect.getMaxY()}; + return contentBoundingRect.size; } } // namespace react diff --git a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h index d2ec294a239e4..6d3dd7e632525 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h +++ b/android/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h @@ -9,7 +9,11 @@ #include +#ifdef ANDROID #include +#include +#include +#endif namespace facebook { namespace react { @@ -21,6 +25,7 @@ class ScrollViewState final { public: Point contentOffset; Rect contentBoundingRect; + int scrollAwayPaddingTop; /* * Returns size of scrollable area. @@ -30,13 +35,19 @@ class ScrollViewState final { #ifdef ANDROID ScrollViewState() = default; ScrollViewState(ScrollViewState const &previousState, folly::dynamic data) - : contentOffset({(Float)data["contentOffsetLeft"].getDouble(), - (Float)data["contentOffsetTop"].getDouble()}), - contentBoundingRect({}){}; + : contentOffset( + {(Float)data["contentOffsetLeft"].getDouble(), + (Float)data["contentOffsetTop"].getDouble()}), + contentBoundingRect({}), + scrollAwayPaddingTop((Float)data["scrollAwayPaddingTop"].getDouble()){}; folly::dynamic getDynamic() const { return folly::dynamic::object("contentOffsetLeft", contentOffset.x)( - "contentOffsetTop", contentOffset.y); + "contentOffsetTop", contentOffset.y)( + "scrollAwayPaddingTop", scrollAwayPaddingTop); + }; + MapBuffer getMapBuffer() const { + return MapBufferBuilder::EMPTY(); }; #endif }; diff --git a/android/ReactCommon/react/renderer/components/scrollview/conversions.h b/android/ReactCommon/react/renderer/components/scrollview/conversions.h index 155e914723ef8..710bd8f3b07de 100644 --- a/android/ReactCommon/react/renderer/components/scrollview/conversions.h +++ b/android/ReactCommon/react/renderer/components/scrollview/conversions.h @@ -9,11 +9,13 @@ #include #include +#include namespace facebook { namespace react { inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, ScrollViewSnapToAlignment &result) { auto string = (std::string)value; @@ -33,6 +35,7 @@ inline void fromRawValue( } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, ScrollViewIndicatorStyle &result) { auto string = (std::string)value; @@ -52,6 +55,7 @@ inline void fromRawValue( } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, ScrollViewKeyboardDismissMode &result) { auto string = (std::string)value; @@ -71,6 +75,7 @@ inline void fromRawValue( } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, ContentInsetAdjustmentBehavior &result) { auto string = (std::string)value; diff --git a/android/ReactCommon/react/renderer/components/slider/Android.mk b/android/ReactCommon/react/renderer/components/slider/Android.mk index 23e58023b81d8..101ed6ee564c1 100644 --- a/android/ReactCommon/react/renderer/components/slider/Android.mk +++ b/android/ReactCommon/react/renderer/components/slider/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_slider +LOCAL_MODULE := rrc_slider LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) $(wildcard $(LOCAL_PATH)/platform/android/react/renderer/components/slider/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ $(LOCAL_PATH)/platform/and LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libfbjni libreact_render_viewmanagers libreact_render_imagemanager libreactnativeutilsjni libreact_render_componentregistry libreact_render_uimanager libreact_render_components_image libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view +LOCAL_SHARED_LIBRARIES := libfbjni libreact_codegen_rncore libreact_render_imagemanager libreactnativeutilsjni libreact_render_componentregistry libreact_render_uimanager librrc_image libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_debug libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -38,3 +38,4 @@ $(call import-module,react/renderer/components/image) $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/uimanager) $(call import-module,yogajni) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/components/slider/BUCK b/android/ReactCommon/react/renderer/components/slider/BUCK index 00f0df782327a..01ac302615e74 100644 --- a/android/ReactCommon/react/renderer/components/slider/BUCK +++ b/android/ReactCommon/react/renderer/components/slider/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", @@ -33,15 +33,10 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/slider", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_tests = [":tests"], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbandroid_exported_headers = subdir_glob( [ @@ -83,6 +78,7 @@ rn_xplat_cxx_library( "//xplat/folly:headers_only", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/components/image:image"), @@ -102,7 +98,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/slider/SliderComponentDescriptor.h b/android/ReactCommon/react/renderer/components/slider/SliderComponentDescriptor.h index 687ea411a0c06..c167f68fa48cb 100644 --- a/android/ReactCommon/react/renderer/components/slider/SliderComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/slider/SliderComponentDescriptor.h @@ -28,10 +28,9 @@ class SliderComponentDescriptor final ? std::make_shared(contextContainer_) : nullptr) {} - void adopt(UnsharedShadowNode shadowNode) const override { + void adopt(ShadowNode::Unshared const &shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); - assert(std::dynamic_pointer_cast(shadowNode)); auto sliderShadowNode = std::static_pointer_cast(shadowNode); diff --git a/android/ReactCommon/react/renderer/components/slider/SliderShadowNode.h b/android/ReactCommon/react/renderer/components/slider/SliderShadowNode.h index 82ab4d0b1dc6c..ebd2eca048360 100644 --- a/android/ReactCommon/react/renderer/components/slider/SliderShadowNode.h +++ b/android/ReactCommon/react/renderer/components/slider/SliderShadowNode.h @@ -43,14 +43,15 @@ class SliderShadowNode final : public ConcreteViewShadowNode< ShadowNodeFamilyFragment const &familyFragment, ComponentDescriptor const &componentDescriptor) { auto imageSource = ImageSource{ImageSource::Type::Invalid}; - return {imageSource, - {imageSource, nullptr}, - imageSource, - {imageSource, nullptr}, - imageSource, - {imageSource, nullptr}, - imageSource, - {imageSource, nullptr}}; + return { + imageSource, + {imageSource, nullptr}, + imageSource, + {imageSource, nullptr}, + imageSource, + {imageSource, nullptr}, + imageSource, + {imageSource, nullptr}}; } #pragma mark - LayoutableShadowNode diff --git a/android/ReactCommon/react/renderer/components/slider/SliderState.h b/android/ReactCommon/react/renderer/components/slider/SliderState.h index 4aa4927faf64c..82a338c08f24e 100644 --- a/android/ReactCommon/react/renderer/components/slider/SliderState.h +++ b/android/ReactCommon/react/renderer/components/slider/SliderState.h @@ -7,7 +7,12 @@ #pragma once +#ifdef ANDROID #include +#include +#include +#endif + #include #include @@ -64,6 +69,9 @@ class SliderState final { folly::dynamic getDynamic() const { return {}; }; + MapBuffer getMapBuffer() const { + return MapBufferBuilder::EMPTY(); + }; #endif private: diff --git a/android/ReactCommon/react/renderer/components/slider/platform/ios/SliderMeasurementsManager.cpp b/android/ReactCommon/react/renderer/components/slider/platform/ios/SliderMeasurementsManager.cpp index b7b930825e224..d23aff739c7af 100644 --- a/android/ReactCommon/react/renderer/components/slider/platform/ios/SliderMeasurementsManager.cpp +++ b/android/ReactCommon/react/renderer/components/slider/platform/ios/SliderMeasurementsManager.cpp @@ -7,13 +7,15 @@ #include "SliderMeasurementsManager.h" +#include + namespace facebook { namespace react { Size SliderMeasurementsManager::measure( SurfaceId surfaceId, LayoutConstraints layoutConstraints) const { - assert(false); // should never reach this point + react_native_assert(false); // should never reach this point return {}; } diff --git a/android/ReactCommon/react/renderer/components/switch/Android.mk b/android/ReactCommon/react/renderer/components/switch/Android.mk index 65be95635a04d..ec7b041a63e24 100644 --- a/android/ReactCommon/react/renderer/components/switch/Android.mk +++ b/android/ReactCommon/react/renderer/components/switch/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_switch +LOCAL_MODULE := rrc_switch LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/androidswitch/react/renderer/components/androidswitch/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/androidswitch/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libfbjni libreact_render_viewmanagers libreactnativeutilsjni libreact_render_componentregistry libreact_render_uimanager libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view +LOCAL_SHARED_LIBRARIES := libfbjni libreact_codegen_rncore libreactnativeutilsjni libreact_render_componentregistry libreact_render_uimanager libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_debug include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactCommon/react/renderer/components/switch/BUCK b/android/ReactCommon/react/renderer/components/switch/BUCK index fa27ee25fe0f5..82ca7c4074eae 100644 --- a/android/ReactCommon/react/renderer/components/switch/BUCK +++ b/android/ReactCommon/react/renderer/components/switch/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", @@ -34,12 +34,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/androidswitch", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_tests = [":tests"], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), @@ -78,7 +72,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/switch/androidswitch/react/renderer/components/androidswitch/AndroidSwitchComponentDescriptor.h b/android/ReactCommon/react/renderer/components/switch/androidswitch/react/renderer/components/androidswitch/AndroidSwitchComponentDescriptor.h index 42a249017da91..b253baf171831 100644 --- a/android/ReactCommon/react/renderer/components/switch/androidswitch/react/renderer/components/androidswitch/AndroidSwitchComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/switch/androidswitch/react/renderer/components/androidswitch/AndroidSwitchComponentDescriptor.h @@ -27,10 +27,9 @@ class AndroidSwitchComponentDescriptor final measurementsManager_(std::make_shared( contextContainer_)) {} - void adopt(UnsharedShadowNode shadowNode) const override { + void adopt(ShadowNode::Unshared const &shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); - assert(std::dynamic_pointer_cast(shadowNode)); auto androidSwitchShadowNode = std::static_pointer_cast(shadowNode); diff --git a/android/ReactCommon/react/renderer/components/text/Android.mk b/android/ReactCommon/react/renderer/components/text/Android.mk index 3e8144673a607..33bfdb78a9636 100644 --- a/android/ReactCommon/react/renderer/components/text/Android.mk +++ b/android/ReactCommon/react/renderer/components/text/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_text +LOCAL_MODULE := rrc_text LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_uimanager libreact_render_textlayoutmanager libreact_render_attributedstring libreact_render_mounting libreact_render_components_view libreact_utils +LOCAL_SHARED_LIBRARIES := libjsi libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_uimanager libreact_render_textlayoutmanager libreact_render_attributedstring libreact_render_mounting librrc_view libreact_utils libreact_debug libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -37,4 +37,6 @@ $(call import-module,react/renderer/textlayoutmanager) $(call import-module,react/renderer/uimanager) $(call import-module,react/renderer/components/view) $(call import-module,react/utils) +$(call import-module,react/debug) +$(call import-module,react/renderer/mapbuffer) $(call import-module,yogajni) diff --git a/android/ReactCommon/react/renderer/components/text/BUCK b/android/ReactCommon/react/renderer/components/text/BUCK index e4962d6f695fe..47f41977a119e 100644 --- a/android/ReactCommon/react/renderer/components/text/BUCK +++ b/android/ReactCommon/react/renderer/components/text/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -36,13 +36,10 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/text", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_tests = [":tests"], + fbandroid_deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), + ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -61,7 +58,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, - react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/attributedstring:attributedstring"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), @@ -71,6 +68,8 @@ rn_xplat_cxx_library( react_native_xplat_target("react/renderer/uimanager:uimanager"), react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), + react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/renderer/telemetry:telemetry"), ], ) @@ -81,7 +80,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], @@ -97,5 +96,6 @@ fb_xplat_cxx_test( ":text", "//xplat/folly:molly", "//xplat/third-party/gmock:gtest", + react_native_xplat_target("react/debug:debug"), ], ) diff --git a/android/ReactCommon/react/renderer/components/text/BaseTextProps.cpp b/android/ReactCommon/react/renderer/components/text/BaseTextProps.cpp index cae5c975a3d50..d8079cf4b812a 100644 --- a/android/ReactCommon/react/renderer/components/text/BaseTextProps.cpp +++ b/android/ReactCommon/react/renderer/components/text/BaseTextProps.cpp @@ -16,6 +16,7 @@ namespace facebook { namespace react { static TextAttributes convertRawProp( + const PropsParserContext &context, const RawProps &rawProps, const TextAttributes sourceTextAttributes, const TextAttributes defaultTextAttributes) { @@ -23,16 +24,19 @@ static TextAttributes convertRawProp( // Color textAttributes.foregroundColor = convertRawProp( + context, rawProps, "color", sourceTextAttributes.foregroundColor, defaultTextAttributes.foregroundColor); textAttributes.backgroundColor = convertRawProp( + context, rawProps, "backgroundColor", sourceTextAttributes.backgroundColor, defaultTextAttributes.backgroundColor); textAttributes.opacity = convertRawProp( + context, rawProps, "opacity", sourceTextAttributes.opacity, @@ -40,58 +44,75 @@ static TextAttributes convertRawProp( // Font textAttributes.fontFamily = convertRawProp( + context, rawProps, "fontFamily", sourceTextAttributes.fontFamily, defaultTextAttributes.fontFamily); textAttributes.fontSize = convertRawProp( + context, rawProps, "fontSize", sourceTextAttributes.fontSize, defaultTextAttributes.fontSize); textAttributes.fontSizeMultiplier = convertRawProp( + context, rawProps, "fontSizeMultiplier", sourceTextAttributes.fontSizeMultiplier, defaultTextAttributes.fontSizeMultiplier); textAttributes.fontWeight = convertRawProp( + context, rawProps, "fontWeight", sourceTextAttributes.fontWeight, defaultTextAttributes.fontWeight); textAttributes.fontStyle = convertRawProp( + context, rawProps, "fontStyle", sourceTextAttributes.fontStyle, defaultTextAttributes.fontStyle); textAttributes.fontVariant = convertRawProp( + context, rawProps, "fontVariant", sourceTextAttributes.fontVariant, defaultTextAttributes.fontVariant); textAttributes.allowFontScaling = convertRawProp( + context, rawProps, "allowFontScaling", sourceTextAttributes.allowFontScaling, defaultTextAttributes.allowFontScaling); textAttributes.letterSpacing = convertRawProp( + context, rawProps, "letterSpacing", sourceTextAttributes.letterSpacing, defaultTextAttributes.letterSpacing); + textAttributes.textTransform = convertRawProp( + context, + rawProps, + "textTransform", + sourceTextAttributes.textTransform, + defaultTextAttributes.textTransform); // Paragraph textAttributes.lineHeight = convertRawProp( + context, rawProps, "lineHeight", sourceTextAttributes.lineHeight, defaultTextAttributes.lineHeight); textAttributes.alignment = convertRawProp( + context, rawProps, "textAlign", sourceTextAttributes.alignment, defaultTextAttributes.alignment); textAttributes.baseWritingDirection = convertRawProp( + context, rawProps, "baseWritingDirection", sourceTextAttributes.baseWritingDirection, @@ -99,21 +120,25 @@ static TextAttributes convertRawProp( // Decoration textAttributes.textDecorationColor = convertRawProp( + context, rawProps, "textDecorationColor", sourceTextAttributes.textDecorationColor, defaultTextAttributes.textDecorationColor); textAttributes.textDecorationLineType = convertRawProp( + context, rawProps, "textDecorationLine", sourceTextAttributes.textDecorationLineType, defaultTextAttributes.textDecorationLineType); textAttributes.textDecorationLineStyle = convertRawProp( + context, rawProps, "textDecorationLineStyle", sourceTextAttributes.textDecorationLineStyle, defaultTextAttributes.textDecorationLineStyle); textAttributes.textDecorationLinePattern = convertRawProp( + context, rawProps, "textDecorationLinePattern", sourceTextAttributes.textDecorationLinePattern, @@ -121,16 +146,19 @@ static TextAttributes convertRawProp( // Shadow textAttributes.textShadowOffset = convertRawProp( + context, rawProps, "textShadowOffset", sourceTextAttributes.textShadowOffset, defaultTextAttributes.textShadowOffset); textAttributes.textShadowRadius = convertRawProp( + context, rawProps, "textShadowRadius", sourceTextAttributes.textShadowRadius, defaultTextAttributes.textShadowRadius); textAttributes.textShadowColor = convertRawProp( + context, rawProps, "textShadowColor", sourceTextAttributes.textShadowColor, @@ -138,12 +166,14 @@ static TextAttributes convertRawProp( // Special textAttributes.isHighlighted = convertRawProp( + context, rawProps, "isHighlighted", sourceTextAttributes.isHighlighted, defaultTextAttributes.isHighlighted); textAttributes.accessibilityRole = convertRawProp( + context, rawProps, "accessibilityRole", sourceTextAttributes.accessibilityRole, @@ -153,9 +183,11 @@ static TextAttributes convertRawProp( } BaseTextProps::BaseTextProps( + const PropsParserContext &context, const BaseTextProps &sourceProps, const RawProps &rawProps) : textAttributes(convertRawProp( + context, rawProps, sourceProps.textAttributes, TextAttributes{})){}; diff --git a/android/ReactCommon/react/renderer/components/text/BaseTextProps.h b/android/ReactCommon/react/renderer/components/text/BaseTextProps.h index 4e4fefbafe583..c20d07b6c3e28 100644 --- a/android/ReactCommon/react/renderer/components/text/BaseTextProps.h +++ b/android/ReactCommon/react/renderer/components/text/BaseTextProps.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -22,7 +23,10 @@ namespace react { class BaseTextProps { public: BaseTextProps() = default; - BaseTextProps(const BaseTextProps &sourceProps, const RawProps &rawProps); + BaseTextProps( + const PropsParserContext &context, + const BaseTextProps &sourceProps, + const RawProps &rawProps); #pragma mark - Props diff --git a/android/ReactCommon/react/renderer/components/text/BaseTextShadowNode.cpp b/android/ReactCommon/react/renderer/components/text/BaseTextShadowNode.cpp index 638317f19a406..a68e2dca773ca 100644 --- a/android/ReactCommon/react/renderer/components/text/BaseTextShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/text/BaseTextShadowNode.cpp @@ -33,7 +33,7 @@ void BaseTextShadowNode::buildAttributedString( for (auto const &childNode : parentNode.getChildren()) { // RawShadowNode auto rawTextShadowNode = - std::dynamic_pointer_cast(childNode); + traitCast(childNode.get()); if (rawTextShadowNode) { auto fragment = AttributedString::Fragment{}; fragment.string = rawTextShadowNode->getConcreteProps().text; @@ -49,8 +49,7 @@ void BaseTextShadowNode::buildAttributedString( } // TextShadowNode - auto textShadowNode = - std::dynamic_pointer_cast(childNode); + auto textShadowNode = traitCast(childNode.get()); if (textShadowNode) { auto localTextAttributes = baseTextAttributes; localTextAttributes.apply( diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h b/android/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h index d2e925f4c8c80..0801a07b8d122 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/text/ParagraphComponentDescriptor.h @@ -7,10 +7,7 @@ #pragma once -#include "ParagraphShadowNode.h" - -#include -#include +#include #include #include #include @@ -31,35 +28,16 @@ class ParagraphComponentDescriptor final textLayoutManager_ = std::make_shared(contextContainer_); } - virtual SharedProps interpolateProps( - float animationProgress, - const SharedProps &props, - const SharedProps &newProps) const override { - SharedProps interpolatedPropsShared = cloneProps(newProps, {}); - - interpolateViewProps( - animationProgress, props, newProps, interpolatedPropsShared); - - return interpolatedPropsShared; - }; - protected: - void adopt(UnsharedShadowNode shadowNode) const override { + void adopt(ShadowNode::Unshared const &shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); - assert(std::dynamic_pointer_cast(shadowNode)); auto paragraphShadowNode = std::static_pointer_cast(shadowNode); // `ParagraphShadowNode` uses `TextLayoutManager` to measure text content // and communicate text rendering metrics to mounting layer. paragraphShadowNode->setTextLayoutManager(textLayoutManager_); - - paragraphShadowNode->dirtyLayout(); - - // All `ParagraphShadowNode`s must have leaf Yoga nodes with properly - // setup measure function. - paragraphShadowNode->enableMeasurement(); } private: diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphProps.cpp b/android/ReactCommon/react/renderer/components/text/ParagraphProps.cpp index a578929150c05..632da3bdcd1a7 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphProps.cpp +++ b/android/ReactCommon/react/renderer/components/text/ParagraphProps.cpp @@ -18,15 +18,24 @@ namespace facebook { namespace react { ParagraphProps::ParagraphProps( + const PropsParserContext &context, ParagraphProps const &sourceProps, RawProps const &rawProps) - : ViewProps(sourceProps, rawProps), - BaseTextProps(sourceProps, rawProps), - paragraphAttributes( - convertRawProp(rawProps, sourceProps.paragraphAttributes, {})), - isSelectable( - convertRawProp(rawProps, "selectable", sourceProps.isSelectable, {})), + : ViewProps(context, sourceProps, rawProps), + BaseTextProps(context, sourceProps, rawProps), + paragraphAttributes(convertRawProp( + context, + rawProps, + sourceProps.paragraphAttributes, + {})), + isSelectable(convertRawProp( + context, + rawProps, + "selectable", + sourceProps.isSelectable, + false)), onTextLayout(convertRawProp( + context, rawProps, "onTextLayout", sourceProps.onTextLayout, diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphProps.h b/android/ReactCommon/react/renderer/components/text/ParagraphProps.h index b456586c71636..a82bbb8eb4bd2 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphProps.h +++ b/android/ReactCommon/react/renderer/components/text/ParagraphProps.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -26,7 +27,10 @@ namespace react { class ParagraphProps : public ViewProps, public BaseTextProps { public: ParagraphProps() = default; - ParagraphProps(ParagraphProps const &sourceProps, RawProps const &rawProps); + ParagraphProps( + const PropsParserContext &context, + ParagraphProps const &sourceProps, + RawProps const &rawProps); #pragma mark - Props diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 87745126463ca..46f4098784165 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -9,11 +9,12 @@ #include +#include #include #include #include #include -#include +#include #include "ParagraphState.h" @@ -77,8 +78,8 @@ Content ParagraphShadowNode::getContentWithMeasuredAttachments( laytableShadowNode->measure(layoutContext, localLayoutConstraints); // Rounding to *next* value on the pixel grid. - size.width += 0.01; - size.height += 0.01; + size.width += 0.01f; + size.height += 0.01f; size = roundToPixel<&ceil>(size, layoutContext.pointScaleFactor); auto fragmentLayoutMetrics = LayoutMetrics{}; @@ -102,19 +103,16 @@ void ParagraphShadowNode::updateStateIfNeeded(Content const &content) { auto &state = getStateData(); - assert(textLayoutManager_); - assert( - (!state.layoutManager || state.layoutManager == textLayoutManager_) && - "`StateData` refers to a different `TextLayoutManager`"); + react_native_assert(textLayoutManager_); - if (state.attributedString == content.attributedString && - state.layoutManager == textLayoutManager_) { + if (state.attributedString == content.attributedString) { return; } - setStateData(ParagraphState{content.attributedString, - content.paragraphAttributes, - textLayoutManager_}); + setStateData(ParagraphState{ + content.attributedString, + content.paragraphAttributes, + textLayoutManager_}); } #pragma mark - LayoutableShadowNode @@ -138,11 +136,6 @@ Size ParagraphShadowNode::measureContent( attributedString.appendFragment({string, textAttributes, {}}); } - auto telemetry = TransactionTelemetry::threadLocalTelemetry(); - if (telemetry) { - telemetry->didMeasureText(); - } - return textLayoutManager_ ->measure( AttributedStringBox{attributedString}, @@ -190,9 +183,10 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { // only to keep it in memory for a while. auto paragraphOwningShadowNode = ShadowNode::Unshared{}; - assert(content.attachments.size() == measurement.attachments.size()); + react_native_assert( + content.attachments.size() == measurement.attachments.size()); - for (auto i = 0; i < content.attachments.size(); i++) { + for (size_t i = 0; i < content.attachments.size(); i++) { auto &attachment = content.attachments.at(i); if (!traitCast(attachment.shadowNode)) { diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h index 4a584edb44a51..5bd8c1b1085ca 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/android/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -28,12 +28,12 @@ extern char const ParagraphComponentName[]; * containing and displaying text. Text content is represented as nested * and components. */ -class ParagraphShadowNode : public ConcreteViewShadowNode< - ParagraphComponentName, - ParagraphProps, - ParagraphEventEmitter, - ParagraphState>, - public BaseTextShadowNode { +class ParagraphShadowNode final : public ConcreteViewShadowNode< + ParagraphComponentName, + ParagraphProps, + ParagraphEventEmitter, + ParagraphState>, + public BaseTextShadowNode { public: using ConcreteViewShadowNode::ConcreteViewShadowNode; @@ -41,6 +41,7 @@ class ParagraphShadowNode : public ConcreteViewShadowNode< auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); traits.set(ShadowNodeTraits::Trait::TextKind); + traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode); #ifdef ANDROID // Unsetting `FormsStackingContext` trait is essential on Android where we diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphState.cpp b/android/ReactCommon/react/renderer/components/text/ParagraphState.cpp index c91bb6e480f41..5c15d2a31e649 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphState.cpp +++ b/android/ReactCommon/react/renderer/components/text/ParagraphState.cpp @@ -17,6 +17,10 @@ namespace react { folly::dynamic ParagraphState::getDynamic() const { return toDynamic(*this); } + +MapBuffer ParagraphState::getMapBuffer() const { + return toMapBuffer(*this); +} #endif } // namespace react diff --git a/android/ReactCommon/react/renderer/components/text/ParagraphState.h b/android/ReactCommon/react/renderer/components/text/ParagraphState.h index 8f320af68e6a1..d4e80fda9cd7c 100644 --- a/android/ReactCommon/react/renderer/components/text/ParagraphState.h +++ b/android/ReactCommon/react/renderer/components/text/ParagraphState.h @@ -7,12 +7,14 @@ #pragma once +#include #include #include #include #ifdef ANDROID #include +#include #endif namespace facebook { @@ -39,14 +41,16 @@ class ParagraphState final { * `TextLayoutManager` provides a connection to platform-specific * text rendering infrastructure which is capable to render the * `AttributedString`. + * This is not on every platform. This is not used on Android, but is + * used on the iOS mounting layer. */ - SharedTextLayoutManager layoutManager; + std::weak_ptr layoutManager; #ifdef ANDROID ParagraphState( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, - SharedTextLayoutManager const &layoutManager) + std::weak_ptr const &layoutManager) : attributedString(attributedString), paragraphAttributes(paragraphAttributes), layoutManager(layoutManager) {} @@ -54,9 +58,10 @@ class ParagraphState final { ParagraphState( ParagraphState const &previousState, folly::dynamic const &data) { - assert(false && "Not supported"); + react_native_assert(false && "Not supported"); }; folly::dynamic getDynamic() const; + MapBuffer getMapBuffer() const; #endif }; diff --git a/android/ReactCommon/react/renderer/components/text/RawTextProps.cpp b/android/ReactCommon/react/renderer/components/text/RawTextProps.cpp index d54292bc527c6..3660824907f04 100644 --- a/android/ReactCommon/react/renderer/components/text/RawTextProps.cpp +++ b/android/ReactCommon/react/renderer/components/text/RawTextProps.cpp @@ -14,10 +14,11 @@ namespace facebook { namespace react { RawTextProps::RawTextProps( + const PropsParserContext &context, const RawTextProps &sourceProps, const RawProps &rawProps) - : Props(sourceProps, rawProps), - text(convertRawProp(rawProps, "text", sourceProps.text, {})){}; + : Props(context, sourceProps, rawProps), + text(convertRawProp(context, rawProps, "text", sourceProps.text, {})){}; #pragma mark - DebugStringConvertible diff --git a/android/ReactCommon/react/renderer/components/text/RawTextProps.h b/android/ReactCommon/react/renderer/components/text/RawTextProps.h index 4b98953a0a262..2516e99b1c498 100644 --- a/android/ReactCommon/react/renderer/components/text/RawTextProps.h +++ b/android/ReactCommon/react/renderer/components/text/RawTextProps.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace facebook { @@ -22,7 +23,10 @@ using SharedRawTextProps = std::shared_ptr; class RawTextProps : public Props { public: RawTextProps() = default; - RawTextProps(const RawTextProps &sourceProps, const RawProps &rawProps); + RawTextProps( + const PropsParserContext &context, + const RawTextProps &sourceProps, + const RawProps &rawProps); #pragma mark - Props diff --git a/android/ReactCommon/react/renderer/components/text/RawTextShadowNode.h b/android/ReactCommon/react/renderer/components/text/RawTextShadowNode.h index b11297ee3533e..8dbde86c996e9 100644 --- a/android/ReactCommon/react/renderer/components/text/RawTextShadowNode.h +++ b/android/ReactCommon/react/renderer/components/text/RawTextShadowNode.h @@ -21,8 +21,42 @@ extern const char RawTextComponentName[]; * is represented as ``. * component must not have any children. */ -using RawTextShadowNode = - ConcreteShadowNode; +class RawTextShadowNode : public ConcreteShadowNode< + RawTextComponentName, + ShadowNode, + RawTextProps> { + public: + using ConcreteShadowNode::ConcreteShadowNode; + static ShadowNodeTraits BaseTraits() { + auto traits = ConcreteShadowNode::BaseTraits(); + traits.set(ShadowNodeTraits::Trait::RawText); + return traits; + } +}; + +template <> +inline RawTextShadowNode const &traitCast( + ShadowNode const &shadowNode) { + bool castable = + shadowNode.getTraits().check(ShadowNodeTraits::Trait::RawText); + react_native_assert(castable); + (void)castable; + return static_cast(shadowNode); +} + +template <> +inline RawTextShadowNode const *traitCast( + ShadowNode const *shadowNode) { + if (!shadowNode) { + return nullptr; + } + bool castable = + shadowNode->getTraits().check(ShadowNodeTraits::Trait::RawText); + if (!castable) { + return nullptr; + } + return static_cast(shadowNode); +} } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/text/TextProps.cpp b/android/ReactCommon/react/renderer/components/text/TextProps.cpp index ac07380c1cdbb..8e4bc4b0a6e94 100644 --- a/android/ReactCommon/react/renderer/components/text/TextProps.cpp +++ b/android/ReactCommon/react/renderer/components/text/TextProps.cpp @@ -10,9 +10,12 @@ namespace facebook { namespace react { -TextProps::TextProps(const TextProps &sourceProps, const RawProps &rawProps) - : Props(sourceProps, rawProps), - BaseTextProps::BaseTextProps(sourceProps, rawProps){}; +TextProps::TextProps( + const PropsParserContext &context, + const TextProps &sourceProps, + const RawProps &rawProps) + : Props(context, sourceProps, rawProps), + BaseTextProps::BaseTextProps(context, sourceProps, rawProps){}; #pragma mark - DebugStringConvertible diff --git a/android/ReactCommon/react/renderer/components/text/TextProps.h b/android/ReactCommon/react/renderer/components/text/TextProps.h index 31e7be2a84fdf..9903b05e0d711 100644 --- a/android/ReactCommon/react/renderer/components/text/TextProps.h +++ b/android/ReactCommon/react/renderer/components/text/TextProps.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -19,7 +20,10 @@ namespace react { class TextProps : public Props, public BaseTextProps { public: TextProps() = default; - TextProps(const TextProps &sourceProps, const RawProps &rawProps); + TextProps( + const PropsParserContext &context, + const TextProps &sourceProps, + const RawProps &rawProps); #pragma mark - DebugStringConvertible diff --git a/android/ReactCommon/react/renderer/components/text/TextShadowNode.h b/android/ReactCommon/react/renderer/components/text/TextShadowNode.h index 15d1fc308c5bc..57e3f25076ba2 100644 --- a/android/ReactCommon/react/renderer/components/text/TextShadowNode.h +++ b/android/ReactCommon/react/renderer/components/text/TextShadowNode.h @@ -34,6 +34,7 @@ class TextShadowNode : public ConcreteShadowNode< #ifdef ANDROID traits.set(ShadowNodeTraits::Trait::FormsView); #endif + traits.set(ShadowNodeTraits::Trait::Text); return traits; } @@ -57,5 +58,27 @@ class TextShadowNode : public ConcreteShadowNode< #endif }; +template <> +inline TextShadowNode const &traitCast( + ShadowNode const &shadowNode) { + bool castable = shadowNode.getTraits().check(ShadowNodeTraits::Trait::Text); + react_native_assert(castable); + (void)castable; + return static_cast(shadowNode); +} + +template <> +inline TextShadowNode const *traitCast( + ShadowNode const *shadowNode) { + if (!shadowNode) { + return nullptr; + } + bool castable = shadowNode->getTraits().check(ShadowNodeTraits::Trait::Text); + if (!castable) { + return nullptr; + } + return static_cast(shadowNode); +} + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/text/conversions.h b/android/ReactCommon/react/renderer/components/text/conversions.h index 0d44cd762143c..8ea5baeddb797 100644 --- a/android/ReactCommon/react/renderer/components/text/conversions.h +++ b/android/ReactCommon/react/renderer/components/text/conversions.h @@ -8,6 +8,10 @@ #include #include #include +#ifdef ANDROID +#include +#include +#endif namespace facebook { namespace react { @@ -21,6 +25,24 @@ inline folly::dynamic toDynamic(ParagraphState const ¶graphState) { newState["hash"] = newState["attributedString"]["hash"]; return newState; } + +// constants for Text State serialization +constexpr static Key TX_STATE_KEY_ATTRIBUTED_STRING = 0; +constexpr static Key TX_STATE_KEY_PARAGRAPH_ATTRIBUTES = 1; +// Used for TextInput +constexpr static Key TX_STATE_KEY_HASH = 2; +constexpr static Key TX_STATE_KEY_MOST_RECENT_EVENT_COUNT = 3; + +inline MapBuffer toMapBuffer(ParagraphState const ¶graphState) { + auto builder = MapBufferBuilder(); + auto attStringMapBuffer = toMapBuffer(paragraphState.attributedString); + builder.putMapBuffer(TX_STATE_KEY_ATTRIBUTED_STRING, attStringMapBuffer); + auto paMapBuffer = toMapBuffer(paragraphState.paragraphAttributes); + builder.putMapBuffer(TX_STATE_KEY_PARAGRAPH_ATTRIBUTES, paMapBuffer); + // TODO: Used for TextInput + builder.putInt(TX_STATE_KEY_HASH, 1234); + return builder.build(); +} #endif } // namespace react diff --git a/android/ReactCommon/react/renderer/components/text/tests/ParagraphLocalDataTest.cpp b/android/ReactCommon/react/renderer/components/text/tests/ParagraphLocalDataTest.cpp index 24e51148450f3..f38bfe8de4f8e 100644 --- a/android/ReactCommon/react/renderer/components/text/tests/ParagraphLocalDataTest.cpp +++ b/android/ReactCommon/react/renderer/components/text/tests/ParagraphLocalDataTest.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -40,12 +41,14 @@ TEST(ParagraphLocalDataTest, testSomething) { auto result = toDynamic(paragraphState)["attributedString"]; - assert(result["string"] == fragment.string); + react_native_assert(result["string"] == fragment.string); auto textAttribute = result["fragments"][0]["textAttributes"]; - assert(textAttribute["foregroundColor"] == toDynamic(text.foregroundColor)); - assert(textAttribute["opacity"] == text.opacity); - assert(textAttribute["fontStyle"] == toString(*text.fontStyle)); - assert(textAttribute["fontWeight"] == toString(*text.fontWeight)); + react_native_assert( + textAttribute["foregroundColor"] == toDynamic(text.foregroundColor)); + react_native_assert(textAttribute["opacity"] == text.opacity); + react_native_assert(textAttribute["fontStyle"] == toString(*text.fontStyle)); + react_native_assert( + textAttribute["fontWeight"] == toString(*text.fontWeight)); } #endif diff --git a/android/ReactCommon/react/renderer/components/textinput/Android.mk b/android/ReactCommon/react/renderer/components/textinput/Android.mk index 80f6bf8fa1aaf..c45affab7b9b8 100644 --- a/android/ReactCommon/react/renderer/components/textinput/Android.mk +++ b/android/ReactCommon/react/renderer/components/textinput/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_textinput +LOCAL_MODULE := rrc_textinput LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/androidtextinput/react/renderer/components/androidtextinput/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/androidtextinput/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_mounting libreact_render_componentregistry libreact_render_debug libreact_render_graphics libreact_render_uimanager libreact_render_imagemanager libreact_render_textlayoutmanager libreact_render_attributedstring libreact_render_components_text libreact_render_components_image libreact_render_components_view libreact_utils +LOCAL_SHARED_LIBRARIES := libjsi libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_mounting libreact_render_componentregistry libreact_render_debug libreact_render_graphics libreact_render_uimanager libreact_render_imagemanager libreact_render_textlayoutmanager libreact_render_attributedstring librrc_text librrc_image librrc_view libreact_utils libreact_debug libreact_render_mapbuffer include $(BUILD_SHARED_LIBRARY) @@ -41,4 +41,6 @@ $(call import-module,react/renderer/components/image) $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/components/text) $(call import-module,react/utils) +$(call import-module,react/debug) $(call import-module,yogajni) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/components/textinput/BUCK b/android/ReactCommon/react/renderer/components/textinput/BUCK index 7667e5515de83..76f69e93aa20a 100644 --- a/android/ReactCommon/react/renderer/components/textinput/BUCK +++ b/android/ReactCommon/react/renderer/components/textinput/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -33,13 +33,10 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/androidtextinput", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_tests = [":tests"], + fbandroid_deps = [ + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), + ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -58,7 +55,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, - react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/attributedstring:attributedstring"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), @@ -70,6 +67,7 @@ rn_xplat_cxx_library( react_native_xplat_target("react/renderer/uimanager:uimanager"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/imagemanager:imagemanager"), + react_native_xplat_target("react/utils:utils"), ], ) @@ -80,7 +78,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h index 26abeff7b997a..1728de336a8e1 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h @@ -7,14 +7,16 @@ #pragma once -#include -#include #include "AndroidTextInputShadowNode.h" +#include + #include #include #include +#include + namespace facebook { namespace react { @@ -76,7 +78,6 @@ class AndroidTextInputComponentDescriptor final {}, {}, {}, - textLayoutManager_, ((YGValue)theme[YGEdgeStart]).value, ((YGValue)theme[YGEdgeEnd]).value, ((YGValue)theme[YGEdgeTop]).value, @@ -85,8 +86,7 @@ class AndroidTextInputComponentDescriptor final } protected: - void adopt(UnsharedShadowNode shadowNode) const override { - assert(std::dynamic_pointer_cast(shadowNode)); + void adopt(ShadowNode::Unshared const &shadowNode) const override { auto textInputShadowNode = std::static_pointer_cast(shadowNode); diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp index c3907cd9a70d4..ce7e24fd476aa 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.cpp @@ -37,215 +37,182 @@ static bool hasValue( } AndroidTextInputProps::AndroidTextInputProps( + const PropsParserContext &context, const AndroidTextInputProps &sourceProps, const RawProps &rawProps) - : ViewProps(sourceProps, rawProps), - BaseTextProps(sourceProps, rawProps), - autoCompleteType(convertRawProp( + : ViewProps(context, sourceProps, rawProps), + BaseTextProps(context, sourceProps, rawProps), + autoComplete(convertRawProp( + context, rawProps, - "autoCompleteType", - sourceProps.autoCompleteType, + "autoComplete", + sourceProps.autoComplete, {})), - returnKeyLabel(convertRawProp( - rawProps, + returnKeyLabel(convertRawProp(context, rawProps, "returnKeyLabel", sourceProps.returnKeyLabel, {})), - numberOfLines(convertRawProp( - rawProps, + numberOfLines(convertRawProp(context, rawProps, "numberOfLines", sourceProps.numberOfLines, {0})), - disableFullscreenUI(convertRawProp( - rawProps, + disableFullscreenUI(convertRawProp(context, rawProps, "disableFullscreenUI", sourceProps.disableFullscreenUI, {false})), - textBreakStrategy(convertRawProp( - rawProps, + textBreakStrategy(convertRawProp(context, rawProps, "textBreakStrategy", sourceProps.textBreakStrategy, {})), - underlineColorAndroid(convertRawProp( - rawProps, + underlineColorAndroid(convertRawProp(context, rawProps, "underlineColorAndroid", sourceProps.underlineColorAndroid, {})), - inlineImageLeft(convertRawProp( - rawProps, + inlineImageLeft(convertRawProp(context, rawProps, "inlineImageLeft", sourceProps.inlineImageLeft, {})), - inlineImagePadding(convertRawProp( - rawProps, + inlineImagePadding(convertRawProp(context, rawProps, "inlineImagePadding", sourceProps.inlineImagePadding, {0})), - importantForAutofill(convertRawProp( - rawProps, + importantForAutofill(convertRawProp(context, rawProps, "importantForAutofill", sourceProps.importantForAutofill, {})), - showSoftInputOnFocus(convertRawProp( - rawProps, + showSoftInputOnFocus(convertRawProp(context, rawProps, "showSoftInputOnFocus", sourceProps.showSoftInputOnFocus, {false})), - autoCapitalize(convertRawProp( - rawProps, + autoCapitalize(convertRawProp(context, rawProps, "autoCapitalize", sourceProps.autoCapitalize, {})), - autoCorrect(convertRawProp( - rawProps, + autoCorrect(convertRawProp(context, rawProps, "autoCorrect", sourceProps.autoCorrect, {false})), - autoFocus(convertRawProp( - rawProps, + autoFocus(convertRawProp(context, rawProps, "autoFocus", sourceProps.autoFocus, {false})), - allowFontScaling(convertRawProp( - rawProps, + allowFontScaling(convertRawProp(context, rawProps, "allowFontScaling", sourceProps.allowFontScaling, {false})), - maxFontSizeMultiplier(convertRawProp( - rawProps, + maxFontSizeMultiplier(convertRawProp(context, rawProps, "maxFontSizeMultiplier", sourceProps.maxFontSizeMultiplier, {0.0})), editable( - convertRawProp(rawProps, "editable", sourceProps.editable, {false})), - keyboardType(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "editable", sourceProps.editable, {false})), + keyboardType(convertRawProp(context, rawProps, "keyboardType", sourceProps.keyboardType, {})), - returnKeyType(convertRawProp( - rawProps, + returnKeyType(convertRawProp(context, rawProps, "returnKeyType", sourceProps.returnKeyType, {})), maxLength( - convertRawProp(rawProps, "maxLength", sourceProps.maxLength, {0})), - multiline(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "maxLength", sourceProps.maxLength, {0})), + multiline(convertRawProp(context, rawProps, "multiline", sourceProps.multiline, {false})), placeholder( - convertRawProp(rawProps, "placeholder", sourceProps.placeholder, {})), - placeholderTextColor(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "placeholder", sourceProps.placeholder, {})), + placeholderTextColor(convertRawProp(context, rawProps, "placeholderTextColor", sourceProps.placeholderTextColor, {})), - secureTextEntry(convertRawProp( - rawProps, + secureTextEntry(convertRawProp(context, rawProps, "secureTextEntry", sourceProps.secureTextEntry, {false})), - selectionColor(convertRawProp( - rawProps, + selectionColor(convertRawProp(context, rawProps, "selectionColor", sourceProps.selectionColor, {})), selection( - convertRawProp(rawProps, "selection", sourceProps.selection, {})), - value(convertRawProp(rawProps, "value", sourceProps.value, {})), - defaultValue(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "selection", sourceProps.selection, {})), + value(convertRawProp(context, rawProps, "value", sourceProps.value, {})), + defaultValue(convertRawProp(context, rawProps, "defaultValue", sourceProps.defaultValue, {})), - selectTextOnFocus(convertRawProp( - rawProps, + selectTextOnFocus(convertRawProp(context, rawProps, "selectTextOnFocus", sourceProps.selectTextOnFocus, {false})), - blurOnSubmit(convertRawProp( - rawProps, + blurOnSubmit(convertRawProp(context, rawProps, "blurOnSubmit", sourceProps.blurOnSubmit, {false})), - caretHidden(convertRawProp( - rawProps, + caretHidden(convertRawProp(context, rawProps, "caretHidden", sourceProps.caretHidden, {false})), - contextMenuHidden(convertRawProp( - rawProps, + contextMenuHidden(convertRawProp(context, rawProps, "contextMenuHidden", sourceProps.contextMenuHidden, {false})), - textShadowColor(convertRawProp( - rawProps, + textShadowColor(convertRawProp(context, rawProps, "textShadowColor", sourceProps.textShadowColor, {})), - textShadowRadius(convertRawProp( - rawProps, + textShadowRadius(convertRawProp(context, rawProps, "textShadowRadius", sourceProps.textShadowRadius, {0.0})), - textDecorationLine(convertRawProp( - rawProps, + textDecorationLine(convertRawProp(context, rawProps, "textDecorationLine", sourceProps.textDecorationLine, {})), fontStyle( - convertRawProp(rawProps, "fontStyle", sourceProps.fontStyle, {})), - textShadowOffset(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "fontStyle", sourceProps.fontStyle, {})), + textShadowOffset(convertRawProp(context, rawProps, "textShadowOffset", sourceProps.textShadowOffset, {})), - lineHeight(convertRawProp( - rawProps, + lineHeight(convertRawProp(context, rawProps, "lineHeight", sourceProps.lineHeight, {0.0})), - textTransform(convertRawProp( - rawProps, + textTransform(convertRawProp(context, rawProps, "textTransform", sourceProps.textTransform, {})), - color(convertRawProp(rawProps, "color", sourceProps.color, {0})), - letterSpacing(convertRawProp( - rawProps, + color(0 /*convertRawProp(context, rawProps, "color", sourceProps.color, {0})*/), + letterSpacing(convertRawProp(context, rawProps, "letterSpacing", sourceProps.letterSpacing, {0.0})), fontSize( - convertRawProp(rawProps, "fontSize", sourceProps.fontSize, {0.0})), + convertRawProp(context, rawProps, "fontSize", sourceProps.fontSize, {0.0})), textAlign( - convertRawProp(rawProps, "textAlign", sourceProps.textAlign, {})), - includeFontPadding(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "textAlign", sourceProps.textAlign, {})), + includeFontPadding(convertRawProp(context, rawProps, "includeFontPadding", sourceProps.includeFontPadding, {false})), fontWeight( - convertRawProp(rawProps, "fontWeight", sourceProps.fontWeight, {})), + convertRawProp(context, rawProps, "fontWeight", sourceProps.fontWeight, {})), fontFamily( - convertRawProp(rawProps, "fontFamily", sourceProps.fontFamily, {})), - textAlignVertical(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "fontFamily", sourceProps.fontFamily, {})), + textAlignVertical(convertRawProp(context, rawProps, "textAlignVertical", sourceProps.textAlignVertical, {})), cursorColor( - convertRawProp(rawProps, "cursorColor", sourceProps.cursorColor, {})), - mostRecentEventCount(convertRawProp( - rawProps, + convertRawProp(context, rawProps, "cursorColor", sourceProps.cursorColor, {})), + mostRecentEventCount(convertRawProp(context, rawProps, "mostRecentEventCount", sourceProps.mostRecentEventCount, {0})), - text(convertRawProp(rawProps, "text", sourceProps.text, {})), + text(convertRawProp(context, rawProps, "text", sourceProps.text, {})), paragraphAttributes( - convertRawProp(rawProps, sourceProps.paragraphAttributes, {})), + convertRawProp(context, rawProps, sourceProps.paragraphAttributes, {})), // See AndroidTextInputComponentDescriptor for usage // TODO T63008435: can these, and this feature, be removed entirely? hasPadding(hasValue(rawProps, sourceProps.hasPadding, "", "padding", "")), @@ -294,7 +261,7 @@ AndroidTextInputProps::AndroidTextInputProps( // TODO T53300085: support this in codegen; this was hand-written folly::dynamic AndroidTextInputProps::getDynamic() const { folly::dynamic props = folly::dynamic::object(); - props["autoCompleteType"] = autoCompleteType; + props["autoComplete"] = autoComplete; props["returnKeyLabel"] = returnKeyLabel; props["numberOfLines"] = numberOfLines; props["disableFullscreenUI"] = disableFullscreenUI; @@ -332,7 +299,7 @@ folly::dynamic AndroidTextInputProps::getDynamic() const { props["textShadowOffset"] = toDynamic(textShadowOffset); props["lineHeight"] = lineHeight; props["textTransform"] = textTransform; - props["color"] = color; + props["color"] = toDynamic(color); props["letterSpacing"] = letterSpacing; props["fontSize"] = fontSize; props["textAlign"] = textAlign; diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h index 18f0bcf4ac60a..34048ddf9100f 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputProps.h @@ -7,6 +7,8 @@ #pragma once +// foo bar 2 + // #include #include #include @@ -16,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -31,17 +34,18 @@ struct AndroidTextInputSelectionStruct { }; static inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, AndroidTextInputSelectionStruct &result) { auto map = (better::map)value; auto start = map.find("start"); if (start != map.end()) { - fromRawValue(start->second, result.start); + fromRawValue(context, start->second, result.start); } auto end = map.find("end"); if (end != map.end()) { - fromRawValue(end->second, result.end); + fromRawValue(context, end->second, result.end); } } @@ -56,17 +60,18 @@ struct AndroidTextInputTextShadowOffsetStruct { }; static inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, AndroidTextInputTextShadowOffsetStruct &result) { auto map = (better::map)value; auto width = map.find("width"); if (width != map.end()) { - fromRawValue(width->second, result.width); + fromRawValue(context, width->second, result.width); } auto height = map.find("height"); if (height != map.end()) { - fromRawValue(height->second, result.height); + fromRawValue(context, height->second, result.height); } } @@ -96,6 +101,7 @@ class AndroidTextInputProps final : public ViewProps, public BaseTextProps { public: AndroidTextInputProps() = default; AndroidTextInputProps( + const PropsParserContext &context, const AndroidTextInputProps &sourceProps, const RawProps &rawProps); @@ -103,7 +109,7 @@ class AndroidTextInputProps final : public ViewProps, public BaseTextProps { #pragma mark - Props - const std::string autoCompleteType{}; + const std::string autoComplete{}; const std::string returnKeyLabel{}; const int numberOfLines{0}; const bool disableFullscreenUI{false}; @@ -141,7 +147,7 @@ class AndroidTextInputProps final : public ViewProps, public BaseTextProps { const AndroidTextInputTextShadowOffsetStruct textShadowOffset{}; const Float lineHeight{0.0}; const std::string textTransform{}; - const int color{0}; + const SharedColor color{0}; const Float letterSpacing{0.0}; const Float fontSize{0.0}; const std::string textAlign{}; diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index 86420fe470da8..1f2e468653e2b 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -8,6 +8,7 @@ #include "AndroidTextInputShadowNode.h" #include +#include #include #include #include @@ -118,16 +119,10 @@ void AndroidTextInputShadowNode::updateStateIfNeeded() { auto reactTreeAttributedString = getAttributedString(); auto const &state = getStateData(); - assert(textLayoutManager_); - assert( - (!state.layoutManager || state.layoutManager == textLayoutManager_) && - "`StateData` refers to a different `TextLayoutManager`"); - // Tree is often out of sync with the value of the TextInput. // This is by design - don't change the value of the TextInput in the State, // and therefore in Java, unless the tree itself changes. - if (state.reactTreeAttributedString == reactTreeAttributedString && - state.layoutManager == textLayoutManager_) { + if (state.reactTreeAttributedString == reactTreeAttributedString) { return; } @@ -155,17 +150,17 @@ void AndroidTextInputShadowNode::updateStateIfNeeded() { // current attributedString unchanged, and pass in zero for the "event count" // so no changes are applied There's no way to prevent a state update from // flowing to Java, so we just ensure it's a noop in those cases. - setStateData(AndroidTextInputState{newEventCount, - newAttributedString, - reactTreeAttributedString, - getConcreteProps().paragraphAttributes, - defaultTextAttributes, - ShadowView(*this), - textLayoutManager_, - state.defaultThemePaddingStart, - state.defaultThemePaddingEnd, - state.defaultThemePaddingTop, - state.defaultThemePaddingBottom}); + setStateData(AndroidTextInputState{ + newEventCount, + newAttributedString, + reactTreeAttributedString, + getConcreteProps().paragraphAttributes, + defaultTextAttributes, + ShadowView(*this), + state.defaultThemePaddingStart, + state.defaultThemePaddingEnd, + state.defaultThemePaddingTop, + state.defaultThemePaddingBottom}); } #pragma mark - LayoutableShadowNode diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h index ac322666e0f45..2642030d94776 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h @@ -24,11 +24,11 @@ extern const char AndroidTextInputComponentName[]; /* * `ShadowNode` for component. */ -class AndroidTextInputShadowNode : public ConcreteViewShadowNode< - AndroidTextInputComponentName, - AndroidTextInputProps, - AndroidTextInputEventEmitter, - AndroidTextInputState> { +class AndroidTextInputShadowNode final : public ConcreteViewShadowNode< + AndroidTextInputComponentName, + AndroidTextInputProps, + AndroidTextInputEventEmitter, + AndroidTextInputState> { public: static ShadowNodeTraits BaseTraits() { auto traits = ConcreteViewShadowNode::BaseTraits(); diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.cpp b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.cpp index 70f89eb40e756..4e51cf0ceef85 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.cpp +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.cpp @@ -20,7 +20,6 @@ AndroidTextInputState::AndroidTextInputState( ParagraphAttributes const ¶graphAttributes, TextAttributes const &defaultTextAttributes, ShadowView const &defaultParentShadowView, - SharedTextLayoutManager const &layoutManager, float defaultThemePaddingStart, float defaultThemePaddingEnd, float defaultThemePaddingTop, @@ -32,7 +31,6 @@ AndroidTextInputState::AndroidTextInputState( paragraphAttributes(paragraphAttributes), defaultTextAttributes(defaultTextAttributes), defaultParentShadowView(defaultParentShadowView), - layoutManager(layoutManager), defaultThemePaddingStart(defaultThemePaddingStart), defaultThemePaddingEnd(defaultThemePaddingEnd), defaultThemePaddingTop(defaultThemePaddingTop), @@ -54,7 +52,6 @@ AndroidTextInputState::AndroidTextInputState( paragraphAttributes(previousState.paragraphAttributes), defaultTextAttributes(previousState.defaultTextAttributes), defaultParentShadowView(previousState.defaultParentShadowView), - layoutManager(previousState.layoutManager), defaultThemePaddingStart(data.getDefault( "themePaddingStart", previousState.defaultThemePaddingStart) diff --git a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.h b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.h index c97ade9565c5e..4d3d41ac7a87c 100644 --- a/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.h +++ b/android/ReactCommon/react/renderer/components/textinput/androidtextinput/react/renderer/components/androidtextinput/AndroidTextInputState.h @@ -13,6 +13,8 @@ #ifdef ANDROID #include +#include +#include #endif namespace facebook { @@ -66,13 +68,6 @@ class AndroidTextInputState final { */ ShadowView defaultParentShadowView; - /* - * `TextLayoutManager` provides a connection to platform-specific - * text rendering infrastructure which is capable to render the - * `AttributedString`. - */ - SharedTextLayoutManager layoutManager{}; - /** * Communicates Android theme padding back to the ShadowNode / Component * Descriptor for layout. @@ -89,7 +84,6 @@ class AndroidTextInputState final { ParagraphAttributes const ¶graphAttributes, TextAttributes const &defaultTextAttributes, ShadowView const &defaultParentShadowView, - SharedTextLayoutManager const &layoutManager, float defaultThemePaddingStart, float defaultThemePaddingEnd, float defaultThemePaddingTop, @@ -100,6 +94,9 @@ class AndroidTextInputState final { AndroidTextInputState const &previousState, folly::dynamic const &data); folly::dynamic getDynamic() const; + MapBuffer getMapBuffer() const { + return MapBufferBuilder::EMPTY(); + }; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/BUCK b/android/ReactCommon/react/renderer/components/textinput/iostextinput/BUCK index 1f7e87f134092..414f2d39855f9 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/BUCK +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -33,12 +33,6 @@ rn_xplat_cxx_library( # TODO(shergin) T26519801 Figure out better directories structure prefix = "react/renderer/components/iostextinput", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_tests = [":tests"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), @@ -58,7 +52,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, - react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/attributedstring:attributedstring"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), @@ -70,6 +64,7 @@ rn_xplat_cxx_library( react_native_xplat_target("react/renderer/uimanager:uimanager"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/imagemanager:imagemanager"), + react_native_xplat_target("react/utils:utils"), ], ) @@ -80,7 +75,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputComponentDescriptor.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputComponentDescriptor.h index 9614fd5712d5b..a23ad76a7b366 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputComponentDescriptor.h @@ -26,16 +26,13 @@ class TextInputComponentDescriptor final } protected: - void adopt(UnsharedShadowNode shadowNode) const override { + void adopt(ShadowNode::Unshared const &shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); - assert(std::dynamic_pointer_cast(shadowNode)); auto concreteShadowNode = std::static_pointer_cast(shadowNode); concreteShadowNode->setTextLayoutManager(textLayoutManager_); - concreteShadowNode->dirtyLayout(); - concreteShadowNode->enableMeasurement(); } private: diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp index e102f463bf405..9e13cb97bb057 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.cpp @@ -16,48 +16,80 @@ namespace facebook { namespace react { TextInputProps::TextInputProps( + const PropsParserContext &context, TextInputProps const &sourceProps, RawProps const &rawProps) - : ViewProps(sourceProps, rawProps), - BaseTextProps(sourceProps, rawProps), - traits(convertRawProp(rawProps, sourceProps.traits, {})), - paragraphAttributes( - convertRawProp(rawProps, sourceProps.paragraphAttributes, {})), + : ViewProps(context, sourceProps, rawProps), + BaseTextProps(context, sourceProps, rawProps), + traits(convertRawProp(context, rawProps, sourceProps.traits, {})), + paragraphAttributes(convertRawProp( + context, + rawProps, + sourceProps.paragraphAttributes, + {})), defaultValue(convertRawProp( + context, rawProps, "defaultValue", sourceProps.defaultValue, {})), - placeholder( - convertRawProp(rawProps, "placeholder", sourceProps.placeholder, {})), + placeholder(convertRawProp( + context, + rawProps, + "placeholder", + sourceProps.placeholder, + {})), placeholderTextColor(convertRawProp( + context, rawProps, "placeholderTextColor", sourceProps.placeholderTextColor, {})), - maxLength( - convertRawProp(rawProps, "maxLength", sourceProps.maxLength, {})), - cursorColor( - convertRawProp(rawProps, "cursorColor", sourceProps.cursorColor, {})), + maxLength(convertRawProp( + context, + rawProps, + "maxLength", + sourceProps.maxLength, + {})), + cursorColor(convertRawProp( + context, + rawProps, + "cursorColor", + sourceProps.cursorColor, + {})), selectionColor(convertRawProp( + context, rawProps, "selectionColor", sourceProps.selectionColor, {})), underlineColorAndroid(convertRawProp( + context, rawProps, "underlineColorAndroid", sourceProps.underlineColorAndroid, {})), - text(convertRawProp(rawProps, "text", sourceProps.text, {})), + text(convertRawProp(context, rawProps, "text", sourceProps.text, {})), mostRecentEventCount(convertRawProp( + context, rawProps, "mostRecentEventCount", sourceProps.mostRecentEventCount, {})), - autoFocus( - convertRawProp(rawProps, "autoFocus", sourceProps.autoFocus, {})), + autoFocus(convertRawProp( + context, + rawProps, + "autoFocus", + sourceProps.autoFocus, + {})), + selection(convertRawProp( + context, + rawProps, + "selection", + sourceProps.selection, + better::optional())), inputAccessoryViewID(convertRawProp( + context, rawProps, "inputAccessoryViewID", sourceProps.inputAccessoryViewID, @@ -89,13 +121,5 @@ ParagraphAttributes TextInputProps::getEffectiveParagraphAttributes() const { return result; } -#ifdef ANDROID -folly::dynamic TextInputProps::getDynamic() const { - folly::dynamic props = folly::dynamic::object(); - props["value"] = value; - return props; -} -#endif - } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h index a12503ff66999..9fb44a4b2c172 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputProps.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,10 @@ namespace react { class TextInputProps final : public ViewProps, public BaseTextProps { public: TextInputProps() = default; - TextInputProps(TextInputProps const &sourceProps, RawProps const &rawProps); + TextInputProps( + const PropsParserContext &context, + TextInputProps const &sourceProps, + RawProps const &rawProps); #pragma mark - Props @@ -54,6 +58,7 @@ class TextInputProps final : public ViewProps, public BaseTextProps { int const mostRecentEventCount{0}; bool autoFocus{false}; + better::optional selection{}; std::string const inputAccessoryViewID{}; @@ -62,10 +67,6 @@ class TextInputProps final : public ViewProps, public BaseTextProps { */ TextAttributes getEffectiveTextAttributes(Float fontSizeMultiplier) const; ParagraphAttributes getEffectiveParagraphAttributes() const; - -#ifdef ANDROID - folly::dynamic getDynamic() const; -#endif }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.cpp b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.cpp index 4e771d9e5e578..fb5c0ff7f33bd 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.cpp @@ -7,6 +7,7 @@ #include "TextInputShadowNode.h" +#include #include #include #include @@ -82,8 +83,8 @@ void TextInputShadowNode::updateStateIfNeeded( auto reactTreeAttributedString = getAttributedString(layoutContext); auto const &state = getStateData(); - assert(textLayoutManager_); - assert( + react_native_assert(textLayoutManager_); + react_native_assert( (!state.layoutManager || state.layoutManager == textLayoutManager_) && "`StateData` refers to a different `TextLayoutManager`"); diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.h index 4924c4f8b6b3b..ffb370aab03f9 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputShadowNode.h @@ -24,12 +24,12 @@ extern const char TextInputComponentName[]; /* * `ShadowNode` for component. */ -class TextInputShadowNode : public ConcreteViewShadowNode< - TextInputComponentName, - TextInputProps, - TextInputEventEmitter, - TextInputState>, - public BaseTextShadowNode { +class TextInputShadowNode final : public ConcreteViewShadowNode< + TextInputComponentName, + TextInputProps, + TextInputEventEmitter, + TextInputState>, + public BaseTextShadowNode { public: using ConcreteViewShadowNode::ConcreteViewShadowNode; @@ -37,6 +37,7 @@ class TextInputShadowNode : public ConcreteViewShadowNode< auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::TextKind); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); + traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode); return traits; } diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputState.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputState.h index 31ea9c77a2aa0..fb249f4a840ab 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputState.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/TextInputState.h @@ -13,6 +13,8 @@ #ifdef ANDROID #include +#include +#include #endif namespace facebook { diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h index 85d2fe926e774..56be93b9da177 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/conversions.h @@ -8,12 +8,14 @@ #pragma once #include +#include #include namespace facebook { namespace react { inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, AutocapitalizationType &result) { auto string = (std::string)value; @@ -36,7 +38,10 @@ inline void fromRawValue( abort(); } -inline void fromRawValue(const RawValue &value, KeyboardAppearance &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + KeyboardAppearance &result) { auto string = (std::string)value; if (string == "default") { result = KeyboardAppearance::Default; @@ -53,7 +58,10 @@ inline void fromRawValue(const RawValue &value, KeyboardAppearance &result) { abort(); } -inline void fromRawValue(const RawValue &value, ReturnKeyType &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + ReturnKeyType &result) { auto string = (std::string)value; if (string == "default") { result = ReturnKeyType::Default; @@ -119,6 +127,7 @@ inline void fromRawValue(const RawValue &value, ReturnKeyType &result) { } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, TextInputAccessoryVisibilityMode &result) { auto string = (std::string)value; @@ -141,7 +150,10 @@ inline void fromRawValue( abort(); } -inline void fromRawValue(const RawValue &value, KeyboardType &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + KeyboardType &result) { auto string = (std::string)value; if (string == "default") { result = KeyboardType::Default; @@ -163,6 +175,10 @@ inline void fromRawValue(const RawValue &value, KeyboardType &result) { result = KeyboardType::NumberPad; return; } + if (string == "url") { + result = KeyboardType::URL; + return; + } if (string == "decimal-pad") { result = KeyboardType::DecimalPad; return; @@ -177,10 +193,6 @@ inline void fromRawValue(const RawValue &value, KeyboardType &result) { result = KeyboardType::NumbersAndPunctuation; return; } - if (string == "url") { - result = KeyboardType::URL; - return; - } if (string == "name-phone-pad") { result = KeyboardType::NamePhonePad; return; diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h index f13e9b44a32e9..f3b0fc2dfc589 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h @@ -77,6 +77,12 @@ enum class KeyboardType { VisiblePassword, }; +class Selection final { + public: + int start{0}; + int end{0}; +}; + /* * Controls features of text inputs. */ diff --git a/android/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h b/android/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h index 4de4dc98d987c..162e989112474 100644 --- a/android/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h +++ b/android/ReactCommon/react/renderer/components/textinput/iostextinput/propsConversions.h @@ -8,107 +8,135 @@ #pragma once #include +#include #include namespace facebook { namespace react { static TextInputTraits convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, TextInputTraits const &sourceTraits, TextInputTraits const &defaultTraits) { auto traits = TextInputTraits{}; traits.multiline = convertRawProp( - rawProps, "multiline", sourceTraits.multiline, defaultTraits.multiline); + context, + rawProps, + "multiline", + sourceTraits.multiline, + defaultTraits.multiline); traits.autocapitalizationType = convertRawProp( + context, rawProps, "autoCapitalize", sourceTraits.autocapitalizationType, defaultTraits.autocapitalizationType); traits.autoCorrect = convertRawProp( + context, rawProps, "autoCorrect", sourceTraits.autoCorrect, defaultTraits.autoCorrect); traits.contextMenuHidden = convertRawProp( + context, rawProps, "contextMenuHidden", sourceTraits.contextMenuHidden, defaultTraits.contextMenuHidden); traits.editable = convertRawProp( - rawProps, "editable", sourceTraits.editable, defaultTraits.editable); + context, + rawProps, + "editable", + sourceTraits.editable, + defaultTraits.editable); traits.enablesReturnKeyAutomatically = convertRawProp( + context, rawProps, "enablesReturnKeyAutomatically", sourceTraits.enablesReturnKeyAutomatically, defaultTraits.enablesReturnKeyAutomatically); traits.keyboardAppearance = convertRawProp( + context, rawProps, "keyboardAppearance", sourceTraits.keyboardAppearance, defaultTraits.keyboardAppearance); traits.spellCheck = convertRawProp( + context, rawProps, "spellCheck", sourceTraits.spellCheck, defaultTraits.spellCheck); traits.caretHidden = convertRawProp( + context, rawProps, "caretHidden", sourceTraits.caretHidden, defaultTraits.caretHidden); traits.clearButtonMode = convertRawProp( + context, rawProps, "clearButtonMode", sourceTraits.clearButtonMode, defaultTraits.clearButtonMode); traits.scrollEnabled = convertRawProp( + context, rawProps, "scrollEnabled", sourceTraits.scrollEnabled, defaultTraits.scrollEnabled); traits.secureTextEntry = convertRawProp( + context, rawProps, "secureTextEntry", sourceTraits.secureTextEntry, defaultTraits.secureTextEntry); traits.blurOnSubmit = convertRawProp( + context, rawProps, "blurOnSubmit", sourceTraits.blurOnSubmit, defaultTraits.blurOnSubmit); traits.clearTextOnFocus = convertRawProp( + context, rawProps, "clearTextOnFocus", sourceTraits.clearTextOnFocus, defaultTraits.clearTextOnFocus); traits.keyboardType = convertRawProp( + context, rawProps, "keyboardType", sourceTraits.keyboardType, defaultTraits.keyboardType); traits.showSoftInputOnFocus = convertRawProp( + context, rawProps, "showSoftInputOnFocus", sourceTraits.showSoftInputOnFocus, defaultTraits.showSoftInputOnFocus); traits.returnKeyType = convertRawProp( + context, rawProps, "returnKeyType", sourceTraits.returnKeyType, defaultTraits.returnKeyType); traits.selectTextOnFocus = convertRawProp( + context, rawProps, "selectTextOnFocus", sourceTraits.selectTextOnFocus, defaultTraits.selectTextOnFocus); traits.textContentType = convertRawProp( + context, rawProps, "textContentType", sourceTraits.textContentType, defaultTraits.textContentType); traits.passwordRules = convertRawProp( + context, rawProps, "passwordRules", sourceTraits.passwordRules, @@ -117,5 +145,38 @@ static TextInputTraits convertRawProp( return traits; } +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + Selection &result) { + if (value.hasType>()) { + auto map = (better::map)value; + for (const auto &pair : map) { + if (pair.first == "start") { + result.start = pair.second; + } else if (pair.first == "end") { + result.end = pair.second; + } else { + LOG(ERROR) << "Unsupported Selection map key: " << pair.first; + react_native_assert(false); + } + } + return; + } + + react_native_assert(value.hasType>()); + if (value.hasType>()) { + auto array = (std::vector)value; + react_native_assert(array.size() == 2); + if (array.size() >= 2) { + result = {array.at(0), array.at(1)}; + } else { + result = {0, 0}; + LOG(ERROR) << "Unsupported Selection vector size: " << array.size(); + } + } else { + LOG(ERROR) << "Unsupported Selection type"; + } +} } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/unimplementedview/Android.mk b/android/ReactCommon/react/renderer/components/unimplementedview/Android.mk index ebdb6de7643e0..959500fb70d13 100644 --- a/android/ReactCommon/react/renderer/components/unimplementedview/Android.mk +++ b/android/ReactCommon/react/renderer/components/unimplementedview/Android.mk @@ -7,7 +7,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_unimplementedview +LOCAL_MODULE := rrc_unimplementedview LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_components_view +LOCAL_SHARED_LIBRARIES := libjsi libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics librrc_view libreact_debug include $(BUILD_SHARED_LIBRARY) @@ -33,3 +33,4 @@ $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) $(call import-module,yogajni) +$(call import-module,react/debug) diff --git a/android/ReactCommon/react/renderer/components/unimplementedview/BUCK b/android/ReactCommon/react/renderer/components/unimplementedview/BUCK index 128195f1d948a..8e59dd374ad0f 100644 --- a/android/ReactCommon/react/renderer/components/unimplementedview/BUCK +++ b/android/ReactCommon/react/renderer/components/unimplementedview/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -29,12 +29,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/unimplementedview", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), labels = ["supermodule:xplat/default/public.react_native.infra"], @@ -53,6 +47,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/components/view:view"), @@ -66,7 +61,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.cpp b/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.cpp index 24946364a4cf1..c6b460641670a 100644 --- a/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.cpp +++ b/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.cpp @@ -20,18 +20,19 @@ ComponentName UnimplementedViewComponentDescriptor::getComponentName() const { } Props::Shared UnimplementedViewComponentDescriptor::cloneProps( + PropsParserContext const &context, Props::Shared const &props, RawProps const &rawProps) const { auto clonedProps = ConcreteComponentDescriptor::cloneProps( - props, rawProps); - assert(std::dynamic_pointer_cast(clonedProps)); + context, props, rawProps); // We have to clone `Props` object one more time to make sure that we have // an unshared (and non-`const`) copy of it which we can mutate. RawProps emptyRawProps{}; - emptyRawProps.parse(rawPropsParser_); + emptyRawProps.parse(rawPropsParser_, context); auto unimplementedViewProps = std::make_shared( + context, *std::static_pointer_cast(clonedProps), emptyRawProps); diff --git a/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.h b/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.h index b2de61f3b1c93..2f5bfca3a95dd 100644 --- a/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewComponentDescriptor.h @@ -9,6 +9,7 @@ #include #include +#include namespace facebook { namespace react { @@ -32,8 +33,10 @@ class UnimplementedViewComponentDescriptor final * In addtion to base implementation, stores a component name inside cloned * `Props` object. */ - Props::Shared cloneProps(Props::Shared const &props, RawProps const &rawProps) - const override; + Props::Shared cloneProps( + PropsParserContext const &context, + Props::Shared const &props, + RawProps const &rawProps) const override; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewProps.h b/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewProps.h index 637769f62f1a7..c2907a50abc38 100644 --- a/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewProps.h +++ b/android/ReactCommon/react/renderer/components/unimplementedview/UnimplementedViewProps.h @@ -8,6 +8,7 @@ #pragma once #include +#include namespace facebook { namespace react { diff --git a/android/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h b/android/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h index 157aebed39bca..23999ae4d28f5 100644 --- a/android/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h +++ b/android/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h @@ -7,7 +7,9 @@ #pragma once +#include #include +#include namespace facebook { namespace react { @@ -31,6 +33,7 @@ enum class AccessibilityTraits : uint32_t { CausesPageTurn = (1 << 14), Header = (1 << 15), Switch = (1 << 16), + TabBar = (1 << 17), }; constexpr enum AccessibilityTraits operator|( @@ -45,6 +48,11 @@ constexpr enum AccessibilityTraits operator&( return (enum AccessibilityTraits)((uint32_t)lhs & (uint32_t)rhs); } +struct AccessibilityAction { + std::string name{""}; + better::optional label{}; +}; + struct AccessibilityState { bool disabled{false}; bool selected{false}; @@ -67,6 +75,26 @@ constexpr bool operator!=( return !(rhs == lhs); } +struct AccessibilityValue { + better::optional min; + better::optional max; + better::optional now; + better::optional text{}; +}; + +constexpr bool operator==( + AccessibilityValue const &lhs, + AccessibilityValue const &rhs) { + return lhs.min == rhs.min && lhs.max == rhs.max && lhs.now == rhs.now && + lhs.text == rhs.text; +} + +constexpr bool operator!=( + AccessibilityValue const &lhs, + AccessibilityValue const &rhs) { + return !(rhs == lhs); +} + enum class ImportantForAccessibility { Auto, Yes, diff --git a/android/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp b/android/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp index aa4c1f47795cd..4dda5214c6dfb 100644 --- a/android/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp +++ b/android/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp @@ -16,79 +16,102 @@ namespace facebook { namespace react { AccessibilityProps::AccessibilityProps( + const PropsParserContext &context, AccessibilityProps const &sourceProps, RawProps const &rawProps) : accessible(convertRawProp( + context, rawProps, "accessible", sourceProps.accessible, false)), accessibilityTraits(convertRawProp( + context, rawProps, "accessibilityRole", sourceProps.accessibilityTraits, AccessibilityTraits::None)), accessibilityState(convertRawProp( + context, rawProps, "accessibilityState", sourceProps.accessibilityState, {})), accessibilityLabel(convertRawProp( + context, rawProps, "accessibilityLabel", sourceProps.accessibilityLabel, "")), accessibilityHint(convertRawProp( + context, rawProps, "accessibilityHint", sourceProps.accessibilityHint, "")), + accessibilityValue(convertRawProp( + context, + rawProps, + "accessibilityValue", + sourceProps.accessibilityValue, + {})), accessibilityActions(convertRawProp( + context, rawProps, "accessibilityActions", sourceProps.accessibilityActions, {})), accessibilityViewIsModal(convertRawProp( + context, rawProps, "accessibilityViewIsModal", sourceProps.accessibilityViewIsModal, false)), accessibilityElementsHidden(convertRawProp( + context, rawProps, "accessibilityElementsHidden", sourceProps.accessibilityElementsHidden, false)), accessibilityIgnoresInvertColors(convertRawProp( + context, rawProps, "accessibilityIgnoresInvertColors", sourceProps.accessibilityIgnoresInvertColors, false)), onAccessibilityTap(convertRawProp( + context, rawProps, "onAccessibilityTap", sourceProps.onAccessibilityTap, {})), onAccessibilityMagicTap(convertRawProp( + context, rawProps, "onAccessibilityMagicTap", sourceProps.onAccessibilityMagicTap, {})), onAccessibilityEscape(convertRawProp( + context, rawProps, "onAccessibilityEscape", sourceProps.onAccessibilityEscape, {})), onAccessibilityAction(convertRawProp( + context, rawProps, "onAccessibilityAction", sourceProps.onAccessibilityAction, {})), importantForAccessibility(convertRawProp( + context, rawProps, "importantForAccessibility", sourceProps.importantForAccessibility, ImportantForAccessibility::Auto)), - testId(convertRawProp(rawProps, "testId", sourceProps.testId, "")) {} + testId( + convertRawProp(context, rawProps, "testID", sourceProps.testId, "")) { +} #pragma mark - DebugStringConvertible diff --git a/android/ReactCommon/react/renderer/components/view/AccessibilityProps.h b/android/ReactCommon/react/renderer/components/view/AccessibilityProps.h index 4235e7cdd6c4f..eab7846966052 100644 --- a/android/ReactCommon/react/renderer/components/view/AccessibilityProps.h +++ b/android/ReactCommon/react/renderer/components/view/AccessibilityProps.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -19,6 +20,7 @@ class AccessibilityProps { public: AccessibilityProps() = default; AccessibilityProps( + const PropsParserContext &context, AccessibilityProps const &sourceProps, RawProps const &rawProps); @@ -29,7 +31,8 @@ class AccessibilityProps { AccessibilityState accessibilityState; std::string accessibilityLabel{""}; std::string accessibilityHint{""}; - std::vector accessibilityActions{}; + AccessibilityValue accessibilityValue; + std::vector accessibilityActions{}; bool accessibilityViewIsModal{false}; bool accessibilityElementsHidden{false}; bool accessibilityIgnoresInvertColors{false}; diff --git a/android/ReactCommon/react/renderer/components/view/Android.mk b/android/ReactCommon/react/renderer/components/view/Android.mk index 8bc37572acb64..2f820d3cc5042 100644 --- a/android/ReactCommon/react/renderer/components/view/Android.mk +++ b/android/ReactCommon/react/renderer/components/view/Android.mk @@ -7,21 +7,21 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := react_render_components_view +LOCAL_MODULE := rrc_view LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) -LOCAL_C_INCLUDES := $(LOCAL_PATH)/ +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics +LOCAL_SHARED_LIBRARIES := libyoga glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_graphics libreact_debug logger libjsi include $(BUILD_SHARED_LIBRARY) @@ -31,4 +31,6 @@ $(call import-module,fbgloginit) $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) +$(call import-module,logger) $(call import-module,yogajni) +$(call import-module,react/debug) diff --git a/android/ReactCommon/react/renderer/components/view/BUCK b/android/ReactCommon/react/renderer/components/view/BUCK index b729ca907cea0..d78b21f2cf89a 100644 --- a/android/ReactCommon/react/renderer/components/view/BUCK +++ b/android/ReactCommon/react/renderer/components/view/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -35,12 +35,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/components/view", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -60,9 +54,11 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/graphics:graphics"), + react_native_xplat_target("logger:logger"), ], ) @@ -73,7 +69,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/components/view/TouchEvent.h b/android/ReactCommon/react/renderer/components/view/TouchEvent.h index 4725fa4eec4d3..02f7ab4e63619 100644 --- a/android/ReactCommon/react/renderer/components/view/TouchEvent.h +++ b/android/ReactCommon/react/renderer/components/view/TouchEvent.h @@ -16,8 +16,6 @@ namespace facebook { namespace react { -using Touches = std::unordered_set; - /* * Defines the `touchstart`, `touchend`, `touchmove`, and `touchcancel` event * types. diff --git a/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp b/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp index 738dec8d6467c..1d8ed3e330409 100644 --- a/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp +++ b/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp @@ -12,8 +12,10 @@ namespace react { #pragma mark - Touches -static jsi::Value touchPayload(jsi::Runtime &runtime, Touch const &touch) { - auto object = jsi::Object(runtime); +static void setTouchPayloadOnObject( + jsi::Object &object, + jsi::Runtime &runtime, + Touch const &touch) { object.setProperty(runtime, "locationX", touch.offsetPoint.x); object.setProperty(runtime, "locationY", touch.offsetPoint.y); object.setProperty(runtime, "pageX", touch.pagePoint.x); @@ -24,7 +26,6 @@ static jsi::Value touchPayload(jsi::Runtime &runtime, Touch const &touch) { object.setProperty(runtime, "target", touch.target); object.setProperty(runtime, "timestamp", touch.timestamp * 1000); object.setProperty(runtime, "force", touch.force); - return object; } static jsi::Value touchesPayload( @@ -33,7 +34,9 @@ static jsi::Value touchesPayload( auto array = jsi::Array(runtime, touches.size()); int i = 0; for (auto const &touch : touches) { - array.setValueAtIndex(runtime, i++, touchPayload(runtime, touch)); + auto object = jsi::Object(runtime); + setTouchPayloadOnObject(object, runtime, touch); + array.setValueAtIndex(runtime, i++, object); } return array; } @@ -48,23 +51,34 @@ static jsi::Value touchEventPayload( runtime, "changedTouches", touchesPayload(runtime, event.changedTouches)); object.setProperty( runtime, "targetTouches", touchesPayload(runtime, event.targetTouches)); + + if (!event.changedTouches.empty()) { + auto const &firstChangedTouch = *event.changedTouches.begin(); + setTouchPayloadOnObject(object, runtime, firstChangedTouch); + } return object; } void TouchEventEmitter::dispatchTouchEvent( std::string const &type, TouchEvent const &event, - EventPriority const &priority) const { + EventPriority priority, + RawEvent::Category category) const { dispatchEvent( type, [event](jsi::Runtime &runtime) { return touchEventPayload(runtime, event); }, - priority); + priority, + category); } void TouchEventEmitter::onTouchStart(TouchEvent const &event) const { - dispatchTouchEvent("touchStart", event, EventPriority::AsynchronousBatched); + dispatchTouchEvent( + "touchStart", + event, + EventPriority::AsynchronousBatched, + RawEvent::Category::ContinuousStart); } void TouchEventEmitter::onTouchMove(TouchEvent const &event) const { @@ -74,11 +88,19 @@ void TouchEventEmitter::onTouchMove(TouchEvent const &event) const { } void TouchEventEmitter::onTouchEnd(TouchEvent const &event) const { - dispatchTouchEvent("touchEnd", event, EventPriority::AsynchronousBatched); + dispatchTouchEvent( + "touchEnd", + event, + EventPriority::AsynchronousBatched, + RawEvent::Category::ContinuousEnd); } void TouchEventEmitter::onTouchCancel(TouchEvent const &event) const { - dispatchTouchEvent("touchCancel", event, EventPriority::AsynchronousBatched); + dispatchTouchEvent( + "touchCancel", + event, + EventPriority::AsynchronousBatched, + RawEvent::Category::ContinuousEnd); } } // namespace react diff --git a/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.h b/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.h index 54a2904903999..c225fbb9d6394 100644 --- a/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.h +++ b/android/ReactCommon/react/renderer/components/view/TouchEventEmitter.h @@ -33,7 +33,8 @@ class TouchEventEmitter : public EventEmitter { void dispatchTouchEvent( std::string const &type, TouchEvent const &event, - EventPriority const &priority) const; + EventPriority priority, + RawEvent::Category category) const; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/view/ViewComponentDescriptor.h b/android/ReactCommon/react/renderer/components/view/ViewComponentDescriptor.h index fd078e656c89b..5379d71b94681 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/components/view/ViewComponentDescriptor.h @@ -9,8 +9,6 @@ #include #include -#include "ViewProps.h" -#include "ViewPropsInterpolation.h" namespace facebook { namespace react { @@ -20,18 +18,6 @@ class ViewComponentDescriptor public: ViewComponentDescriptor(ComponentDescriptorParameters const ¶meters) : ConcreteComponentDescriptor(parameters) {} - - virtual SharedProps interpolateProps( - float animationProgress, - const SharedProps &props, - const SharedProps &newProps) const override { - SharedProps interpolatedPropsShared = cloneProps(newProps, {}); - - interpolateViewProps( - animationProgress, props, newProps, interpolatedPropsShared); - - return interpolatedPropsShared; - }; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp b/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp index a81f85af595ca..f978934cc239b 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp +++ b/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp @@ -15,7 +15,7 @@ namespace react { void ViewEventEmitter::onAccessibilityAction(const std::string &name) const { dispatchEvent("accessibilityAction", [name](jsi::Runtime &runtime) { auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "action", name); + payload.setProperty(runtime, "actionName", name); return payload; }); } @@ -35,31 +35,72 @@ void ViewEventEmitter::onAccessibilityEscape() const { #pragma mark - Layout void ViewEventEmitter::onLayout(const LayoutMetrics &layoutMetrics) const { - // Due to State Reconciliation, `onLayout` can be called potentially many - // times with identical layoutMetrics. Ensure that the JS event is only - // dispatched when the value changes. + // A copy of a shared pointer (`layoutEventState_`) establishes shared + // ownership that will be captured by lambda. + auto layoutEventState = layoutEventState_; + + // Dispatched `frame` values to JavaScript thread are throttled here. + // Basic ideas: + // - Scheduling a lambda with some value that already was dispatched, does + // nothing. + // - If some lambda is already in flight, we don't schedule another; + // - When a lambda is being executed on the JavaScript thread, the *most + // recent* `frame` value is used (not the value that was current at the + // moment of scheduling the lambda). + // + // This implies the following caveats: + // - Some events can be skipped; + // - When values change rapidly, even events with different values + // can be skipped (only the very last will be delivered). + // - Ordering is preserved. + { - std::lock_guard guard(layoutMetricsMutex_); - if (lastLayoutMetrics_ == layoutMetrics) { + std::lock_guard guard(layoutEventState->mutex); + + // If a *particular* `frame` was already dispatched to the JavaScript side, + // no other work is required. + if (layoutEventState->frame == layoutMetrics.frame && + layoutEventState->wasDispatched) { + return; + } + + // If the *particular* `frame` was not already dispatched *or* + // some *other* `frame` was dispatched before, + // we need to schedule the dispatching. + layoutEventState->wasDispatched = false; + layoutEventState->frame = layoutMetrics.frame; + + // Something is already in flight, dispatching another event is not + // required. + if (layoutEventState->isDispatching) { return; } - lastLayoutMetrics_ = layoutMetrics; - } - auto expectedEventCount = ++*eventCounter_; + layoutEventState->isDispatching = true; + } - // dispatchUniqueEvent only drops consecutive onLayout events to the same - // node. We want to drop *any* unprocessed onLayout events when there's a - // newer one. dispatchEvent( "layout", - [frame = layoutMetrics.frame, - expectedEventCount, - eventCounter = eventCounter_](jsi::Runtime &runtime) { - auto actualEventCount = eventCounter->load(); - if (expectedEventCount != actualEventCount) { - // Drop stale events - return jsi::Value::null(); + [layoutEventState](jsi::Runtime &runtime) { + auto frame = Rect{}; + + { + std::lock_guard guard(layoutEventState->mutex); + + layoutEventState->isDispatching = false; + + // If some *particular* `frame` was already dispatched before, + // and since then there were no other new values of the `frame` + // observed, do nothing. + if (layoutEventState->wasDispatched) { + return jsi::Value::null(); + } + + frame = layoutEventState->frame; + + // If some *particular* `frame` was *not* already dispatched before, + // it's time to dispatch it and mark as dispatched. + layoutEventState->wasDispatched = true; } auto layout = jsi::Object(runtime); @@ -70,7 +111,8 @@ void ViewEventEmitter::onLayout(const LayoutMetrics &layoutMetrics) const { auto payload = jsi::Object(runtime); payload.setProperty(runtime, "layout", std::move(layout)); return jsi::Value(std::move(payload)); - }); + }, + EventPriority::AsynchronousUnbatched); } } // namespace react diff --git a/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.h b/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.h index c00802431a18c..1a4e17e84e515 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.h +++ b/android/ReactCommon/react/renderer/components/view/ViewEventEmitter.h @@ -38,11 +38,35 @@ class ViewEventEmitter : public TouchEventEmitter { void onLayout(const LayoutMetrics &layoutMetrics) const; private: - mutable std::mutex layoutMetricsMutex_; - mutable LayoutMetrics lastLayoutMetrics_; + /* + * Contains the most recent `frame` and a `mutex` protecting access to it. + */ + struct LayoutEventState { + /* + * Protects an access to other fields of the struct. + */ + std::mutex mutex; - mutable std::shared_ptr eventCounter_{ - std::make_shared(0)}; + /* + * Last dispatched `frame` value or value that's being dispatched right now. + */ + Rect frame{}; + + /* + * Indicates that the `frame` value was already dispatched (and dispatching + * of the *same* value is not needed). + */ + bool wasDispatched{false}; + + /* + * Indicates that some lambda is already being dispatching (and dispatching + * another one is not needed). + */ + bool isDispatching{false}; + }; + + mutable std::shared_ptr layoutEventState_{ + std::make_shared()}; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/components/view/ViewProps.cpp b/android/ReactCommon/react/renderer/components/view/ViewProps.cpp index a7c4a4b0b7238..0e1a997679390 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewProps.cpp +++ b/android/ReactCommon/react/renderer/components/view/ViewProps.cpp @@ -18,83 +18,131 @@ namespace facebook { namespace react { -ViewProps::ViewProps(ViewProps const &sourceProps, RawProps const &rawProps) - : YogaStylableProps(sourceProps, rawProps), - AccessibilityProps(sourceProps, rawProps), - opacity( - convertRawProp(rawProps, "opacity", sourceProps.opacity, (Float)1.0)), +ViewProps::ViewProps( + const PropsParserContext &context, + ViewProps const &sourceProps, + RawProps const &rawProps) + : YogaStylableProps(context, sourceProps, rawProps), + AccessibilityProps(context, sourceProps, rawProps), + opacity(convertRawProp( + context, + rawProps, + "opacity", + sourceProps.opacity, + (Float)1.0)), foregroundColor(convertRawProp( + context, rawProps, "foregroundColor", sourceProps.foregroundColor, {})), backgroundColor(convertRawProp( + context, rawProps, "backgroundColor", sourceProps.backgroundColor, {})), borderRadii(convertRawProp( + context, rawProps, "border", "Radius", sourceProps.borderRadii, {})), borderColors(convertRawProp( + context, rawProps, "border", "Color", sourceProps.borderColors, {})), borderStyles(convertRawProp( + context, rawProps, "border", "Style", sourceProps.borderStyles, {})), - shadowColor( - convertRawProp(rawProps, "shadowColor", sourceProps.shadowColor, {})), + shadowColor(convertRawProp( + context, + rawProps, + "shadowColor", + sourceProps.shadowColor, + {})), shadowOffset(convertRawProp( + context, rawProps, "shadowOffset", sourceProps.shadowOffset, {})), shadowOpacity(convertRawProp( + context, rawProps, "shadowOpacity", sourceProps.shadowOpacity, {})), shadowRadius(convertRawProp( + context, rawProps, "shadowRadius", sourceProps.shadowRadius, {})), - transform( - convertRawProp(rawProps, "transform", sourceProps.transform, {})), + transform(convertRawProp( + context, + rawProps, + "transform", + sourceProps.transform, + {})), backfaceVisibility(convertRawProp( + context, rawProps, "backfaceVisibility", sourceProps.backfaceVisibility, {})), shouldRasterize(convertRawProp( + context, rawProps, "shouldRasterize", sourceProps.shouldRasterize, {})), - zIndex(convertRawProp(rawProps, "zIndex", sourceProps.zIndex, {})), + zIndex( + convertRawProp(context, rawProps, "zIndex", sourceProps.zIndex, {})), pointerEvents(convertRawProp( + context, rawProps, "pointerEvents", sourceProps.pointerEvents, {})), - hitSlop(convertRawProp(rawProps, "hitSlop", sourceProps.hitSlop, {})), - onLayout(convertRawProp(rawProps, "onLayout", sourceProps.onLayout, {})), + hitSlop(convertRawProp( + context, + rawProps, + "hitSlop", + sourceProps.hitSlop, + {})), + onLayout(convertRawProp( + context, + rawProps, + "onLayout", + sourceProps.onLayout, + {})), collapsable(convertRawProp( + context, rawProps, "collapsable", sourceProps.collapsable, true)), - elevation( - convertRawProp(rawProps, "elevation", sourceProps.elevation, {})){}; + removeClippedSubviews(convertRawProp( + context, + rawProps, + "removeClippedSubviews", + sourceProps.removeClippedSubviews, + false)), + elevation(convertRawProp( + context, + rawProps, + "elevation", + sourceProps.elevation, + {})){}; #pragma mark - Convenience Methods diff --git a/android/ReactCommon/react/renderer/components/view/ViewProps.h b/android/ReactCommon/react/renderer/components/view/ViewProps.h index 997353b23b772..b66249a6915c2 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewProps.h +++ b/android/ReactCommon/react/renderer/components/view/ViewProps.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,10 @@ using SharedViewProps = std::shared_ptr; class ViewProps : public YogaStylableProps, public AccessibilityProps { public: ViewProps() = default; - ViewProps(ViewProps const &sourceProps, RawProps const &rawProps); + ViewProps( + const PropsParserContext &context, + ViewProps const &sourceProps, + RawProps const &rawProps); #pragma mark - Props @@ -59,6 +63,8 @@ class ViewProps : public YogaStylableProps, public AccessibilityProps { bool collapsable{true}; + bool removeClippedSubviews{false}; + Float elevation{}; /* Android-only */ #pragma mark - Convenience Methods diff --git a/android/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h b/android/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h index 0470d696d514c..cc4567abc6962 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h +++ b/android/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h @@ -7,7 +7,8 @@ #pragma once -#include "ViewProps.h" +#include +#include namespace facebook { namespace react { @@ -23,15 +24,11 @@ static inline void interpolateViewProps( const SharedProps &newPropsShared, SharedProps &interpolatedPropsShared) { ViewProps const *oldViewProps = - dynamic_cast(oldPropsShared.get()); + static_cast(oldPropsShared.get()); ViewProps const *newViewProps = - dynamic_cast(newPropsShared.get()); + static_cast(newPropsShared.get()); ViewProps *interpolatedProps = const_cast( - dynamic_cast(interpolatedPropsShared.get())); - - assert( - oldViewProps != nullptr && newViewProps != nullptr && - interpolatedProps != nullptr); + static_cast(interpolatedPropsShared.get())); interpolatedProps->opacity = oldViewProps->opacity + (newViewProps->opacity - oldViewProps->opacity) * animationProgress; diff --git a/android/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp b/android/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp index 124babc4c195a..75fa7f4c73a6a 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp @@ -28,14 +28,6 @@ ViewShadowNode::ViewShadowNode( initialize(); } -static bool isColorMeaningful(SharedColor const &color) noexcept { - if (!color) { - return false; - } - - return colorComponentsFromColor(color).alpha > 0; -} - void ViewShadowNode::initialize() noexcept { auto &viewProps = static_cast(*props_); @@ -50,20 +42,15 @@ void ViewShadowNode::initialize() noexcept { viewProps.getClipsContentToBounds() || isColorMeaningful(viewProps.shadowColor) || viewProps.accessibilityElementsHidden || - viewProps.importantForAccessibility != ImportantForAccessibility::Auto; + viewProps.accessibilityViewIsModal || + viewProps.importantForAccessibility != ImportantForAccessibility::Auto || + viewProps.removeClippedSubviews; - bool formsView = isColorMeaningful(viewProps.backgroundColor) || + bool formsView = formsStackingContext || + isColorMeaningful(viewProps.backgroundColor) || isColorMeaningful(viewProps.foregroundColor) || - !(viewProps.yogaStyle.border() == YGStyle::Edges{}); - - formsView = formsView || formsStackingContext; - -#ifdef ANDROID - // Force `formsStackingContext` trait for nodes which have `formsView`. - // TODO: T63560216 Investigate why/how `formsView` entangled with - // `formsStackingContext`. - formsStackingContext = formsStackingContext || formsView; -#endif + !(viewProps.yogaStyle.border() == YGStyle::Edges{}) || + !viewProps.testId.empty(); if (formsView) { traits_.set(ShadowNodeTraits::Trait::FormsView); diff --git a/android/ReactCommon/react/renderer/components/view/ViewShadowNode.h b/android/ReactCommon/react/renderer/components/view/ViewShadowNode.h index 0ccf2116e4477..20ccc04d5f450 100644 --- a/android/ReactCommon/react/renderer/components/view/ViewShadowNode.h +++ b/android/ReactCommon/react/renderer/components/view/ViewShadowNode.h @@ -24,7 +24,7 @@ class ViewShadowNode final : public ConcreteViewShadowNode< ViewEventEmitter> { public: static ShadowNodeTraits BaseTraits() { - auto traits = BaseShadowNode::BaseTraits(); + auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::View); return traits; } diff --git a/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index 60f867e1ee00f..0c5ccdf083c79 100644 --- a/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -6,6 +6,9 @@ */ #include "YogaLayoutableShadowNode.h" +#include +#include +#include #include #include #include @@ -20,25 +23,43 @@ namespace facebook { namespace react { -thread_local LayoutContext threadLocalLayoutContext; - -static void applyLayoutConstraints( - YGStyle &yogaStyle, - LayoutConstraints const &layoutConstraints) { - yogaStyle.minDimensions()[YGDimensionWidth] = - yogaStyleValueFromFloat(layoutConstraints.minimumSize.width); - yogaStyle.minDimensions()[YGDimensionHeight] = - yogaStyleValueFromFloat(layoutConstraints.minimumSize.height); - - yogaStyle.maxDimensions()[YGDimensionWidth] = - yogaStyleValueFromFloat(layoutConstraints.maximumSize.width); - yogaStyle.maxDimensions()[YGDimensionHeight] = - yogaStyleValueFromFloat(layoutConstraints.maximumSize.height); +static int FabricDefaultYogaLog( + const YGConfigRef, + const YGNodeRef, + YGLogLevel level, + const char *format, + va_list args) { + va_list args_copy; + va_copy(args_copy, args); + + // Adding 1 to add space for terminating null character. + int size_s = vsnprintf(NULL, 0, format, args); + auto size = static_cast(size_s); + std::vector buffer(size); + + vsnprintf(buffer.data(), size, format, args_copy); + switch (level) { + case YGLogLevelError: + react_native_log_error(buffer.data()); + break; + case YGLogLevelFatal: + react_native_log_fatal(buffer.data()); + break; + case YGLogLevelWarn: + react_native_log_warn(buffer.data()); + break; + case YGLogLevelInfo: + case YGLogLevelDebug: + case YGLogLevelVerbose: + default: + react_native_log_info(buffer.data()); + } - yogaStyle.direction() = - yogaDirectionFromLayoutDirection(layoutConstraints.layoutDirection); + return size_s; } +thread_local LayoutContext threadLocalLayoutContext; + ShadowNodeTraits YogaLayoutableShadowNode::BaseTraits() { auto traits = LayoutableShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::YogaLayoutableKind); @@ -50,7 +71,7 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode( ShadowNodeFamily::Shared const &family, ShadowNodeTraits traits) : LayoutableShadowNode(fragment, family, traits), - yogaConfig_(nullptr), + yogaConfig_(FabricDefaultYogaLog), yogaNode_(&initializeYogaConfig(yogaConfig_)) { yogaNode_.setContext(this); @@ -58,6 +79,14 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode( // This is not a default for `YGNode`. yogaNode_.setDirty(true); + if (getTraits().check(ShadowNodeTraits::Trait::MeasurableYogaNode)) { + react_native_assert( + getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)); + + yogaNode_.setMeasureFunc( + YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector); + } + updateYogaProps(); updateYogaChildren(); @@ -68,19 +97,31 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode( ShadowNode const &sourceShadowNode, ShadowNodeFragment const &fragment) : LayoutableShadowNode(sourceShadowNode, fragment), - yogaConfig_(nullptr), + yogaConfig_(FabricDefaultYogaLog), yogaNode_( static_cast(sourceShadowNode) .yogaNode_, &initializeYogaConfig(yogaConfig_)) { + // Note, cloned `YGNode` instance (copied using copy-constructor) inherits + // dirty flag, measure function, and other properties being set originally in + // the `YogaLayoutableShadowNode` constructor above. + + react_native_assert( + static_cast(sourceShadowNode) + .yogaNode_.isDirty() == yogaNode_.isDirty() && + "Yoga node must inherit dirty flag."); + yogaNode_.setContext(this); yogaNode_.setOwner(nullptr); updateYogaChildrenOwnersIfNeeded(); - // Yoga node must inherit dirty flag. - assert( - static_cast(sourceShadowNode) - .yogaNode_.isDirty() == yogaNode_.isDirty()); + // This is the only legit place where we can dirty cloned Yoga node. + // If we do it later, ancestor nodes will not be able to observe this and + // dirty (and clone) themselves as a result. + if (getTraits().check(ShadowNodeTraits::Trait::DirtyYogaNode) || + getTraits().check(ShadowNodeTraits::Trait::MeasurableYogaNode)) { + yogaNode_.setDirty(true); + } if (fragment.props) { updateYogaProps(); @@ -116,14 +157,16 @@ void YogaLayoutableShadowNode::enableMeasurement() { void YogaLayoutableShadowNode::appendYogaChild(ShadowNode const &childNode) { // The caller must check this before calling this method. - assert(!getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)); + react_native_assert( + !getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)); ensureYogaChildrenLookFine(); auto &layoutableChildNode = traitCast(childNode); yogaNode_.insertChild( - &layoutableChildNode.yogaNode_, yogaNode_.getChildren().size()); + &layoutableChildNode.yogaNode_, + static_cast(yogaNode_.getChildren().size())); ensureYogaChildrenLookFine(); } @@ -133,13 +176,14 @@ void YogaLayoutableShadowNode::adoptYogaChild(size_t index) { ensureYogaChildrenLookFine(); // The caller must check this before calling this method. - assert(!getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)); + react_native_assert( + !getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)); auto &children = getChildren(); // Overflow checks. - assert(children.size() > index); - assert(children.size() >= yogaNode_.getChildren().size()); + react_native_assert(children.size() > index); + react_native_assert(children.size() >= yogaNode_.getChildren().size()); auto &childNode = *children.at(index); @@ -149,13 +193,14 @@ void YogaLayoutableShadowNode::adoptYogaChild(size_t index) { // Note, the following (commented out) assert is conceptually valid but still // might produce false-positive signals because of the ABA problem (different // objects with non-interleaving life-times being allocated on the same - // address). assert(layoutableChildNode.yogaNode_.getOwner() != &yogaNode_); + // address). react_native_assert(layoutableChildNode.yogaNode_.getOwner() != + // &yogaNode_); if (layoutableChildNode.yogaNode_.getOwner() == nullptr) { // The child node is not owned. layoutableChildNode.yogaNode_.setOwner(&yogaNode_); // At this point the child yoga node must be already inserted by the caller. - // assert(layoutableChildNode.yogaNode_.isDirty()); + // react_native_assert(layoutableChildNode.yogaNode_.isDirty()); } else { // The child is owned by some other node, we need to clone that. auto clonedChildNode = childNode.clone({}); @@ -163,16 +208,18 @@ void YogaLayoutableShadowNode::adoptYogaChild(size_t index) { traitCast(*clonedChildNode); // The owner must be nullptr for a newly cloned node. - assert(layoutableClonedChildNode.yogaNode_.getOwner() == nullptr); + react_native_assert( + layoutableClonedChildNode.yogaNode_.getOwner() == nullptr); // Establishing ownership. layoutableClonedChildNode.yogaNode_.setOwner(&yogaNode_); // Replace the child node with a newly cloned one in the children list. - replaceChild(childNode, clonedChildNode, index); + replaceChild(childNode, clonedChildNode, static_cast(index)); // Replace the Yoga node inside the Yoga node children list. - yogaNode_.replaceChild(&layoutableClonedChildNode.yogaNode_, index); + yogaNode_.replaceChild( + &layoutableClonedChildNode.yogaNode_, static_cast(index)); } ensureYogaChildrenLookFine(); @@ -198,19 +245,26 @@ void YogaLayoutableShadowNode::appendChild( yogaNode_.setDirty(true); // All children of a non-leaf `YogaLayoutableShadowNode` must be a - // `YogaLayoutableShadowNode`s. - assert(traitCast(childNode.get())); - - // Appending the Yoga node. - appendYogaChild(*childNode); - - ensureYogaChildrenLookFine(); - ensureYogaChildrenAlighment(); - - // Adopting the Yoga node. - adoptYogaChild(getChildren().size() - 1); - - ensureConsistency(); + // `YogaLayoutableShadowNode`s to be appended. This happens when invalid + // string/numeric child is passed which is not YogaLayoutableShadowNode + // (e.g. RCTRawText). This used to throw an error, but we are ignoring it + // because we want core library components to be fault-tolerant and degrade + // gracefully. A soft error will be emitted from JavaScript. + if (traitCast(childNode.get())) { + // Appending the Yoga node. + appendYogaChild(*childNode); + + ensureYogaChildrenLookFine(); + ensureYogaChildrenAlighment(); + + // Adopting the Yoga node. + adoptYogaChild(getChildren().size() - 1); + + ensureConsistency(); + } else { + react_native_log_error( + "Text strings must be rendered within a component."); + } } bool YogaLayoutableShadowNode::doesOwn( @@ -254,7 +308,7 @@ void YogaLayoutableShadowNode::updateYogaChildren() { } } - assert(getChildren().size() == yogaNode_.getChildren().size()); + react_native_assert(getChildren().size() == yogaNode_.getChildren().size()); yogaNode_.setDirty(!isClean); } @@ -331,7 +385,59 @@ void YogaLayoutableShadowNode::layoutTree( */ yogaConfig_.pointScaleFactor = layoutContext.pointScaleFactor; - applyLayoutConstraints(yogaNode_.getStyle(), layoutConstraints); + auto minimumSize = layoutConstraints.minimumSize; + auto maximumSize = layoutConstraints.maximumSize; + + // The caller must ensure that layout constraints make sense. + // Values cannot be NaN. + react_native_assert(!std::isnan(minimumSize.width)); + react_native_assert(!std::isnan(minimumSize.height)); + react_native_assert(!std::isnan(maximumSize.width)); + react_native_assert(!std::isnan(maximumSize.height)); + // Values cannot be negative. + react_native_assert(minimumSize.width >= 0); + react_native_assert(minimumSize.height >= 0); + react_native_assert(maximumSize.width >= 0); + react_native_assert(maximumSize.height >= 0); + // Mimimum size cannot be infinity. + react_native_assert(!std::isinf(minimumSize.width)); + react_native_assert(!std::isinf(minimumSize.height)); + + // Internally Yoga uses three different measurement modes controlling layout + // constraints: `Undefined`, `Exactly`, and `AtMost`. These modes are an + // implementation detail and are not defined in `CSS Flexible Box Layout + // Module`. Yoga C++ API (and `YGNodeCalculateLayout` function particularly) + // does not allow to specify the measure modes explicitly. Instead, it infers + // these from styles associated with the root node. + // To pass the actual layout constraints to Yoga we represent them as + // `(min/max)(Height/Width)` style properties. Also, we pass `ownerWidth` & + // `ownerHeight` to allow proper calculation of relative (e.g. specified in + // percents) style values. + + auto &yogaStyle = yogaNode_.getStyle(); + + auto ownerWidth = yogaFloatFromFloat(maximumSize.width); + auto ownerHeight = yogaFloatFromFloat(maximumSize.height); + + yogaStyle.maxDimensions()[YGDimensionWidth] = std::isfinite(maximumSize.width) + ? yogaStyleValueFromFloat(maximumSize.width) + : YGValueUndefined; + + yogaStyle.maxDimensions()[YGDimensionHeight] = + std::isfinite(maximumSize.height) + ? yogaStyleValueFromFloat(maximumSize.height) + : YGValueUndefined; + + yogaStyle.minDimensions()[YGDimensionWidth] = minimumSize.width > 0 + ? yogaStyleValueFromFloat(minimumSize.width) + : YGValueUndefined; + + yogaStyle.minDimensions()[YGDimensionHeight] = minimumSize.height > 0 + ? yogaStyleValueFromFloat(minimumSize.height) + : YGValueUndefined; + + auto direction = + yogaDirectionFromLayoutDirection(layoutConstraints.layoutDirection); threadLocalLayoutContext = layoutContext; @@ -341,9 +447,7 @@ void YogaLayoutableShadowNode::layoutTree( { SystraceSection s("YogaLayoutableShadowNode::YGNodeCalculateLayout"); - - YGNodeCalculateLayout( - &yogaNode_, YGUndefined, YGUndefined, YGDirectionInherit); + YGNodeCalculateLayout(&yogaNode_, ownerWidth, ownerHeight, direction); } if (yogaNode_.getHasNewLayout()) { @@ -372,7 +476,7 @@ static EdgeInsets calculateOverflowInset( void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) { // Reading data from a dirtied node does not make sense. - assert(!yogaNode_.isDirty()); + react_native_assert(!yogaNode_.isDirty()); auto contentFrame = Rect{}; @@ -381,17 +485,17 @@ void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) { *static_cast(childYogaNode->getContext()); // Verifying that the Yoga node belongs to the ShadowNode. - assert(&childNode.yogaNode_ == childYogaNode); + react_native_assert(&childNode.yogaNode_ == childYogaNode); if (childYogaNode->getHasNewLayout()) { childYogaNode->setHasNewLayout(false); // Reading data from a dirtied node does not make sense. - assert(!childYogaNode->isDirty()); + react_native_assert(!childYogaNode->isDirty()); // We must copy layout metrics from Yoga node only once (when the parent // node exclusively ownes the child node). - assert(childYogaNode->getOwner() == &yogaNode_); + react_native_assert(childYogaNode->getOwner() == &yogaNode_); // We are about to mutate layout metrics of the node. childNode.ensureUnsealed(); @@ -446,9 +550,10 @@ YGNode *YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector( auto oldNode = static_cast(oldYogaNode->getContext()); - auto clonedNode = oldNode->clone({ShadowNodeFragment::propsPlaceholder(), - ShadowNodeFragment::childrenPlaceholder(), - oldNode->getState()}); + auto clonedNode = oldNode->clone( + {ShadowNodeFragment::propsPlaceholder(), + ShadowNodeFragment::childrenPlaceholder(), + oldNode->getState()}); parentNode->replaceChild(*oldNode, clonedNode, childIndex); return &static_cast(*clonedNode).yogaNode_; } @@ -466,8 +571,9 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector( static_cast(yogaNode->getContext()); auto minimumSize = Size{0, 0}; - auto maximumSize = Size{std::numeric_limits::infinity(), - std::numeric_limits::infinity()}; + auto maximumSize = Size{ + std::numeric_limits::infinity(), + std::numeric_limits::infinity()}; switch (widthMode) { case YGMeasureModeUndefined: @@ -496,32 +602,16 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector( auto size = shadowNodeRawPtr->measureContent( threadLocalLayoutContext, {minimumSize, maximumSize}); - return YGSize{yogaFloatFromFloat(size.width), - yogaFloatFromFloat(size.height)}; + return YGSize{ + yogaFloatFromFloat(size.width), yogaFloatFromFloat(size.height)}; } -#ifdef RN_DEBUG_YOGA_LOGGER -static int YogaLog( - const YGConfigRef config, - const YGNodeRef node, - YGLogLevel level, - const char *format, - va_list args) { - int result = vsnprintf(NULL, 0, format, args); - std::vector buffer(1 + result); - vsnprintf(buffer.data(), buffer.size(), format, args); - LOG(INFO) << "RNYogaLogger " << buffer.data(); - return result; -} -#endif - YGConfig &YogaLayoutableShadowNode::initializeYogaConfig(YGConfig &config) { config.setCloneNodeCallback( YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector); config.useLegacyStretchBehaviour = true; #ifdef RN_DEBUG_YOGA_LOGGER config.printTree = true; - config.setLogger(&YogaLog); #endif return config; } @@ -653,7 +743,7 @@ void YogaLayoutableShadowNode::ensureConsistency() const { } void YogaLayoutableShadowNode::ensureYogaChildrenOwnersConsistency() const { -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG // Checking that all Yoga node children have the same `owner`. // The owner might be not equal to the `yogaNode_` though. auto &yogaChildren = yogaNode_.getChildren(); @@ -661,14 +751,14 @@ void YogaLayoutableShadowNode::ensureYogaChildrenOwnersConsistency() const { if (!yogaChildren.empty()) { auto owner = yogaChildren.at(0)->getOwner(); for (auto const &child : yogaChildren) { - assert(child->getOwner() == owner); + react_native_assert(child->getOwner() == owner); } } #endif } void YogaLayoutableShadowNode::ensureYogaChildrenLookFine() const { -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG // Checking that the shapes of Yoga node children object look fine. // This is the only heuristic that might produce false-positive results // (really broken dangled nodes might look fine). This is useful as an early @@ -676,17 +766,17 @@ void YogaLayoutableShadowNode::ensureYogaChildrenLookFine() const { auto &yogaChildren = yogaNode_.getChildren(); for (auto const &yogaChild : yogaChildren) { - assert(yogaChild->getContext()); - assert(yogaChild->getChildren().size() < 16384); + react_native_assert(yogaChild->getContext()); + react_native_assert(yogaChild->getChildren().size() < 16384); if (!yogaChild->getChildren().empty()) { - assert(!yogaChild->hasMeasureFunc()); + react_native_assert(!yogaChild->hasMeasureFunc()); } } #endif } void YogaLayoutableShadowNode::ensureYogaChildrenAlighment() const { -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG // If the node is not a leaf node, checking that: // - All children are `YogaLayoutableShadowNode` subclasses. // - All Yoga children are owned/connected to corresponding children of @@ -696,16 +786,16 @@ void YogaLayoutableShadowNode::ensureYogaChildrenAlighment() const { auto &children = getChildren(); if (getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)) { - assert(yogaChildren.empty()); + react_native_assert(yogaChildren.empty()); return; } - assert(yogaChildren.size() == children.size()); + react_native_assert(yogaChildren.size() == children.size()); for (size_t i = 0; i < children.size(); i++) { auto &yogaChild = yogaChildren.at(i); auto &child = children.at(i); - assert( + react_native_assert( yogaChild->getContext() == traitCast(child.get())); } diff --git a/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h b/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h index 634c839df4815..77ba813114589 100644 --- a/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h +++ b/android/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h @@ -12,6 +12,7 @@ #include +#include #include #include #include @@ -149,8 +150,8 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode { * - borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius * - border(Left|Right)Width → border(Start|End)Width * - border(Left|Right)Color → border(Start|End)Color - * This is neccesarry to be backwards compatible with Paper, it swaps the - * values as well in https://fburl.com/diffusion/kl7bjr3h + * This is neccesarry to be backwards compatible with old renderer, it swaps + * the values as well in https://fburl.com/diffusion/kl7bjr3h */ static void swapLeftAndRightInTree( YogaLayoutableShadowNode const &shadowNode); @@ -185,10 +186,7 @@ inline YogaLayoutableShadowNode const & traitCast(ShadowNode const &shadowNode) { bool castable = shadowNode.getTraits().check(ShadowNodeTraits::Trait::YogaLayoutableKind); - assert( - castable == - (dynamic_cast(&shadowNode) != nullptr)); - assert(castable); + react_native_assert(castable); (void)castable; return static_cast(shadowNode); } @@ -201,9 +199,6 @@ traitCast(ShadowNode const *shadowNode) { } bool castable = shadowNode->getTraits().check( ShadowNodeTraits::Trait::YogaLayoutableKind); - assert( - castable == - (dynamic_cast(shadowNode) != nullptr)); if (!castable) { return nullptr; } diff --git a/android/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp b/android/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp index 8fe909a188ace..d8c44f49f5c16 100644 --- a/android/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp +++ b/android/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp @@ -20,10 +20,11 @@ namespace facebook { namespace react { YogaStylableProps::YogaStylableProps( + const PropsParserContext &context, YogaStylableProps const &sourceProps, RawProps const &rawProps) - : Props(sourceProps, rawProps), - yogaStyle(convertRawProp(rawProps, sourceProps.yogaStyle)){}; + : Props(context, sourceProps, rawProps), + yogaStyle(convertRawProp(context, rawProps, sourceProps.yogaStyle)){}; #pragma mark - DebugStringConvertible diff --git a/android/ReactCommon/react/renderer/components/view/YogaStylableProps.h b/android/ReactCommon/react/renderer/components/view/YogaStylableProps.h index d272f0693461b..3d6e314d6c0fb 100644 --- a/android/ReactCommon/react/renderer/components/view/YogaStylableProps.h +++ b/android/ReactCommon/react/renderer/components/view/YogaStylableProps.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace facebook { @@ -19,6 +20,7 @@ class YogaStylableProps : public Props { public: YogaStylableProps() = default; YogaStylableProps( + const PropsParserContext &context, YogaStylableProps const &sourceProps, RawProps const &rawProps); diff --git a/android/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h b/android/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h index 35a92168e99e6..92c52527cd55d 100644 --- a/android/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h +++ b/android/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h @@ -8,7 +8,10 @@ #pragma once #include +#include +#include #include +#include #include namespace facebook { @@ -19,7 +22,7 @@ inline void fromString(const std::string &string, AccessibilityTraits &result) { result = AccessibilityTraits::None; return; } - if (string == "button") { + if (string == "button" || string == "togglebutton") { result = AccessibilityTraits::Button; return; } @@ -91,37 +94,53 @@ inline void fromString(const std::string &string, AccessibilityTraits &result) { result = AccessibilityTraits::Switch; return; } + if (string == "tabbar") { + result = AccessibilityTraits::TabBar; + return; + } + if (string == "progressbar") { + result = AccessibilityTraits::UpdatesFrequently; + return; + } result = AccessibilityTraits::None; } -inline void fromRawValue(const RawValue &value, AccessibilityTraits &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + AccessibilityTraits &result) { if (value.hasType()) { fromString((std::string)value, result); return; } + result = {}; + + react_native_assert(value.hasType>()); if (value.hasType>()) { - result = {}; auto items = (std::vector)value; for (auto &item : items) { AccessibilityTraits itemAccessibilityTraits; fromString(item, itemAccessibilityTraits); result = result | itemAccessibilityTraits; } + } else { + LOG(ERROR) << "AccessibilityTraits parsing: unsupported type"; } - - abort(); } -inline void fromRawValue(const RawValue &value, AccessibilityState &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + AccessibilityState &result) { auto map = (better::map)value; auto selected = map.find("selected"); if (selected != map.end()) { - fromRawValue(selected->second, result.selected); + fromRawValue(context, selected->second, result.selected); } auto disabled = map.find("disabled"); if (disabled != map.end()) { - fromRawValue(disabled->second, result.disabled); + fromRawValue(context, disabled->second, result.disabled); } auto checked = map.find("checked"); if (checked != map.end()) { @@ -143,11 +162,11 @@ inline void fromRawValue(const RawValue &value, AccessibilityState &result) { } auto busy = map.find("busy"); if (busy != map.end()) { - fromRawValue(busy->second, result.busy); + fromRawValue(context, busy->second, result.busy); } auto expanded = map.find("expanded"); if (expanded != map.end()) { - fromRawValue(expanded->second, result.expanded); + fromRawValue(context, expanded->second, result.expanded); } } @@ -166,26 +185,82 @@ inline std::string toString( } inline void fromRawValue( + const PropsParserContext &context, const RawValue &value, ImportantForAccessibility &result) { - auto string = (std::string)value; - if (string == "auto") { - result = ImportantForAccessibility::Auto; - return; + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "auto") { + result = ImportantForAccessibility::Auto; + } else if (string == "yes") { + result = ImportantForAccessibility::Yes; + } else if (string == "no") { + result = ImportantForAccessibility::No; + } else if (string == "no-hide-descendants") { + result = ImportantForAccessibility::NoHideDescendants; + } else { + LOG(ERROR) << "Unsupported ImportantForAccessiblity value: " << string; + react_native_assert(false); + } + } else { + LOG(ERROR) << "Unsupported ImportantForAccessiblity type"; } - if (string == "yes") { - result = ImportantForAccessibility::Yes; - return; +} + +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + AccessibilityAction &result) { + auto map = (better::map)value; + + auto name = map.find("name"); + react_native_assert(name != map.end() && name->second.hasType()); + if (name != map.end()) { + fromRawValue(context, name->second, result.name); } - if (string == "no") { - result = ImportantForAccessibility::No; - return; + + auto label = map.find("label"); + if (label != map.end()) { + if (label->second.hasType()) { + result.label = (std::string)label->second; + } } - if (string == "no-hide-descendants") { - result = ImportantForAccessibility::NoHideDescendants; - return; +} + +inline void fromRawValue( + const PropsParserContext &, + const RawValue &value, + AccessibilityValue &result) { + auto map = (better::map)value; + + auto min = map.find("min"); + if (min != map.end()) { + if (min->second.hasType()) { + result.min = (int)min->second; + } + } + + auto max = map.find("max"); + if (max != map.end()) { + if (max->second.hasType()) { + result.max = (int)max->second; + } + } + + auto now = map.find("now"); + if (now != map.end()) { + if (now->second.hasType()) { + result.now = (int)now->second; + } + } + + auto text = map.find("text"); + if (text != map.end()) { + if (text->second.hasType()) { + result.text = (std::string)text->second; + } } - abort(); } } // namespace react diff --git a/android/ReactCommon/react/renderer/components/view/conversions.h b/android/ReactCommon/react/renderer/components/view/conversions.h index 93eb658be343e..36fc9d507f136 100644 --- a/android/ReactCommon/react/renderer/components/view/conversions.h +++ b/android/ReactCommon/react/renderer/components/view/conversions.h @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -47,7 +49,7 @@ inline Float floatFromYogaFloat(float value) { } inline float yogaFloatFromFloat(Float value) { - if (std::isinf(value)) { + if (!std::isfinite(value)) { return YGUndefined; } @@ -89,7 +91,7 @@ inline YGFloatOptional yogaOptionalFloatFromFloat(Float value) { inline YGValue yogaStyleValueFromFloat( const Float &value, YGUnit unit = YGUnitPoint) { - if (std::isnan(value)) { + if (!std::isfinite(value)) { return YGValueUndefined; } @@ -117,11 +119,13 @@ inline better::optional optionalFloatFromYogaValue( inline LayoutMetrics layoutMetricsFromYogaNode(YGNode &yogaNode) { auto layoutMetrics = LayoutMetrics{}; - layoutMetrics.frame = - Rect{Point{floatFromYogaFloat(YGNodeLayoutGetLeft(&yogaNode)), - floatFromYogaFloat(YGNodeLayoutGetTop(&yogaNode))}, - Size{floatFromYogaFloat(YGNodeLayoutGetWidth(&yogaNode)), - floatFromYogaFloat(YGNodeLayoutGetHeight(&yogaNode))}}; + layoutMetrics.frame = Rect{ + Point{ + floatFromYogaFloat(YGNodeLayoutGetLeft(&yogaNode)), + floatFromYogaFloat(YGNodeLayoutGetTop(&yogaNode))}, + Size{ + floatFromYogaFloat(YGNodeLayoutGetWidth(&yogaNode)), + floatFromYogaFloat(YGNodeLayoutGetHeight(&yogaNode))}}; layoutMetrics.borderWidth = EdgeInsets{ floatFromYogaFloat(YGNodeLayoutGetBorder(&yogaNode, YGEdgeLeft)), @@ -162,8 +166,11 @@ inline YGDirection yogaDirectionFromLayoutDirection(LayoutDirection direction) { } } -inline void fromRawValue(const RawValue &value, YGDirection &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGDirection &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "inherit") { result = YGDirectionInherit; @@ -178,11 +185,14 @@ inline void fromRawValue(const RawValue &value, YGDirection &result) { return; } LOG(FATAL) << "Could not parse YGDirection:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGFlexDirection &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGFlexDirection &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "row") { result = YGFlexDirectionRow; @@ -201,11 +211,14 @@ inline void fromRawValue(const RawValue &value, YGFlexDirection &result) { return; } LOG(FATAL) << "Could not parse YGFlexDirection:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGJustify &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGJustify &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "flex-start") { result = YGJustifyFlexStart; @@ -232,11 +245,14 @@ inline void fromRawValue(const RawValue &value, YGJustify &result) { return; } LOG(FATAL) << "Could not parse YGJustify:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGAlign &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGAlign &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "auto") { result = YGAlignAuto; @@ -271,11 +287,14 @@ inline void fromRawValue(const RawValue &value, YGAlign &result) { return; } LOG(FATAL) << "Could not parse YGAlign:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGPositionType &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGPositionType &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "static") { result = YGPositionTypeStatic; @@ -290,11 +309,14 @@ inline void fromRawValue(const RawValue &value, YGPositionType &result) { return; } LOG(FATAL) << "Could not parse YGPositionType:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGWrap &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGWrap &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "nowrap") { result = YGWrapNoWrap; @@ -309,11 +331,14 @@ inline void fromRawValue(const RawValue &value, YGWrap &result) { return; } LOG(FATAL) << "Could not parse YGWrap:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGOverflow &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGOverflow &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "visible") { result = YGOverflowVisible; @@ -328,11 +353,14 @@ inline void fromRawValue(const RawValue &value, YGOverflow &result) { return; } LOG(FATAL) << "Could not parse YGOverflow:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGDisplay &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGDisplay &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "flex") { result = YGDisplayFlex; @@ -343,10 +371,13 @@ inline void fromRawValue(const RawValue &value, YGDisplay &result) { return; } LOG(FATAL) << "Could not parse YGDisplay:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, YGStyle::ValueRepr &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGStyle::ValueRepr &result) { if (value.hasType()) { result = yogaStyleValueFromFloat((Float)value); return; @@ -370,7 +401,10 @@ inline void fromRawValue(const RawValue &value, YGStyle::ValueRepr &result) { result = YGValueUndefined; } -inline void fromRawValue(const RawValue &value, YGFloatOptional &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + YGFloatOptional &result) { if (value.hasType()) { result = YGFloatOptional((float)value); return; @@ -382,27 +416,30 @@ inline void fromRawValue(const RawValue &value, YGFloatOptional &result) { } } LOG(FATAL) << "Could not parse YGFloatOptional"; - assert(false); + react_native_assert(false); } inline Float toRadians(const RawValue &value) { if (value.hasType()) { return (Float)value; } - assert(value.hasType()); + react_native_assert(value.hasType()); auto stringValue = (std::string)value; char *suffixStart; double num = strtod( stringValue.c_str(), &suffixStart); // can't use std::stod, probably // because of old Android NDKs if (0 == strncmp(suffixStart, "deg", 3)) { - return num * M_PI / 180; + return static_cast(num * M_PI / 180.0f); } - return num; // assume suffix is "rad" + return static_cast(num); // assume suffix is "rad" } -inline void fromRawValue(const RawValue &value, Transform &result) { - assert(value.hasType>()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + Transform &result) { + react_native_assert(value.hasType>()); auto transformMatrix = Transform{}; auto configurations = static_cast>(value); @@ -420,9 +457,9 @@ inline void fromRawValue(const RawValue &value, Transform &result) { auto ¶meters = pair->second; if (operation == "matrix") { - assert(parameters.hasType>()); + react_native_assert(parameters.hasType>()); auto numbers = (std::vector)parameters; - assert(numbers.size() == transformMatrix.matrix.size()); + react_native_assert(numbers.size() == transformMatrix.matrix.size()); auto i = 0; for (auto number : numbers) { transformMatrix.matrix[i++] = number; @@ -476,8 +513,11 @@ inline void fromRawValue(const RawValue &value, Transform &result) { result = transformMatrix; } -inline void fromRawValue(const RawValue &value, PointerEventsMode &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + PointerEventsMode &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "auto") { result = PointerEventsMode::Auto; @@ -496,11 +536,14 @@ inline void fromRawValue(const RawValue &value, PointerEventsMode &result) { return; } LOG(FATAL) << "Could not parse PointerEventsMode:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, BackfaceVisibility &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + BackfaceVisibility &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "auto") { result = BackfaceVisibility::Auto; @@ -515,11 +558,14 @@ inline void fromRawValue(const RawValue &value, BackfaceVisibility &result) { return; } LOG(FATAL) << "Could not parse BackfaceVisibility:" << stringValue; - assert(false); + react_native_assert(false); } -inline void fromRawValue(const RawValue &value, BorderStyle &result) { - assert(value.hasType()); +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + BorderStyle &result) { + react_native_assert(value.hasType()); auto stringValue = (std::string)value; if (stringValue == "solid") { result = BorderStyle::Solid; @@ -534,7 +580,7 @@ inline void fromRawValue(const RawValue &value, BorderStyle &result) { return; } LOG(FATAL) << "Could not parse BorderStyle:" << stringValue; - assert(false); + react_native_assert(false); } inline std::string toString( diff --git a/android/ReactCommon/react/renderer/components/view/primitives.h b/android/ReactCommon/react/renderer/components/view/primitives.h index f50f37e71fa9e..52e5f3f08c218 100644 --- a/android/ReactCommon/react/renderer/components/view/primitives.h +++ b/android/ReactCommon/react/renderer/components/view/primitives.h @@ -38,18 +38,19 @@ struct CascadedRectangleEdges { OptionalT all{}; Counterpart resolve(bool isRTL, T defaults) const { - const auto leading = isRTL ? end : start; - const auto trailing = isRTL ? start : end; + const auto leadingEdge = isRTL ? end : start; + const auto trailingEdge = isRTL ? start : end; const auto horizontalOrAllOrDefault = horizontal.value_or(all.value_or(defaults)); const auto verticalOrAllOrDefault = vertical.value_or(all.value_or(defaults)); return { - /* .left = */ left.value_or(leading.value_or(horizontalOrAllOrDefault)), + /* .left = */ + left.value_or(leadingEdge.value_or(horizontalOrAllOrDefault)), /* .top = */ top.value_or(verticalOrAllOrDefault), /* .right = */ - right.value_or(trailing.value_or(horizontalOrAllOrDefault)), + right.value_or(trailingEdge.value_or(horizontalOrAllOrDefault)), /* .bottom = */ bottom.value_or(verticalOrAllOrDefault), }; } diff --git a/android/ReactCommon/react/renderer/components/view/propsConversions.h b/android/ReactCommon/react/renderer/components/view/propsConversions.h index 89acb27348a63..5f91adc5cbdae 100644 --- a/android/ReactCommon/react/renderer/components/view/propsConversions.h +++ b/android/ReactCommon/react/renderer/components/view/propsConversions.h @@ -8,12 +8,14 @@ #pragma once #include +#include #include namespace facebook { namespace react { static inline YGStyle::Dimensions convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, char const *widthName, char const *heightName, @@ -21,11 +23,13 @@ static inline YGStyle::Dimensions convertRawProp( YGStyle::Dimensions const &defaultValue) { auto dimensions = defaultValue; dimensions[YGDimensionWidth] = convertRawProp( + context, rawProps, widthName, sourceValue[YGDimensionWidth], defaultValue[YGDimensionWidth]); dimensions[YGDimensionHeight] = convertRawProp( + context, rawProps, heightName, sourceValue[YGDimensionHeight], @@ -34,6 +38,7 @@ static inline YGStyle::Dimensions convertRawProp( } static inline YGStyle::Edges convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, char const *prefix, char const *suffix, @@ -41,6 +46,7 @@ static inline YGStyle::Edges convertRawProp( YGStyle::Edges const &defaultValue) { auto result = defaultValue; result[YGEdgeLeft] = convertRawProp( + context, rawProps, "Left", sourceValue[YGEdgeLeft], @@ -48,6 +54,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeTop] = convertRawProp( + context, rawProps, "Top", sourceValue[YGEdgeTop], @@ -55,6 +62,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeRight] = convertRawProp( + context, rawProps, "Right", sourceValue[YGEdgeRight], @@ -62,6 +70,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeBottom] = convertRawProp( + context, rawProps, "Bottom", sourceValue[YGEdgeBottom], @@ -69,6 +78,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeStart] = convertRawProp( + context, rawProps, "Start", sourceValue[YGEdgeStart], @@ -76,6 +86,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeEnd] = convertRawProp( + context, rawProps, "End", sourceValue[YGEdgeEnd], @@ -83,6 +94,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeHorizontal] = convertRawProp( + context, rawProps, "Horizontal", sourceValue[YGEdgeHorizontal], @@ -90,6 +102,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeVertical] = convertRawProp( + context, rawProps, "Vertical", sourceValue[YGEdgeVertical], @@ -97,6 +110,7 @@ static inline YGStyle::Edges convertRawProp( prefix, suffix); result[YGEdgeAll] = convertRawProp( + context, rawProps, "", sourceValue[YGEdgeAll], @@ -107,99 +121,177 @@ static inline YGStyle::Edges convertRawProp( } static inline YGStyle::Edges convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, YGStyle::Edges const &sourceValue, YGStyle::Edges const &defaultValue) { auto result = defaultValue; result[YGEdgeLeft] = convertRawProp( - rawProps, "left", sourceValue[YGEdgeLeft], defaultValue[YGEdgeLeft]); + context, + rawProps, + "left", + sourceValue[YGEdgeLeft], + defaultValue[YGEdgeLeft]); result[YGEdgeTop] = convertRawProp( - rawProps, "top", sourceValue[YGEdgeTop], defaultValue[YGEdgeTop]); + context, + rawProps, + "top", + sourceValue[YGEdgeTop], + defaultValue[YGEdgeTop]); result[YGEdgeRight] = convertRawProp( - rawProps, "right", sourceValue[YGEdgeRight], defaultValue[YGEdgeRight]); + context, + rawProps, + "right", + sourceValue[YGEdgeRight], + defaultValue[YGEdgeRight]); result[YGEdgeBottom] = convertRawProp( + context, rawProps, "bottom", sourceValue[YGEdgeBottom], defaultValue[YGEdgeBottom]); result[YGEdgeStart] = convertRawProp( - rawProps, "start", sourceValue[YGEdgeStart], defaultValue[YGEdgeStart]); + context, + rawProps, + "start", + sourceValue[YGEdgeStart], + defaultValue[YGEdgeStart]); result[YGEdgeEnd] = convertRawProp( - rawProps, "end", sourceValue[YGEdgeEnd], defaultValue[YGEdgeEnd]); + context, + rawProps, + "end", + sourceValue[YGEdgeEnd], + defaultValue[YGEdgeEnd]); return result; } static inline YGStyle convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, YGStyle const &sourceValue) { auto yogaStyle = YGStyle{}; yogaStyle.direction() = convertRawProp( - rawProps, "direction", sourceValue.direction(), yogaStyle.direction()); + context, + rawProps, + "direction", + sourceValue.direction(), + yogaStyle.direction()); yogaStyle.flexDirection() = convertRawProp( + context, rawProps, "flexDirection", sourceValue.flexDirection(), yogaStyle.flexDirection()); yogaStyle.justifyContent() = convertRawProp( + context, rawProps, "justifyContent", sourceValue.justifyContent(), yogaStyle.justifyContent()); yogaStyle.alignContent() = convertRawProp( + context, rawProps, "alignContent", sourceValue.alignContent(), yogaStyle.alignContent()); yogaStyle.alignItems() = convertRawProp( - rawProps, "alignItems", sourceValue.alignItems(), yogaStyle.alignItems()); + context, + rawProps, + "alignItems", + sourceValue.alignItems(), + yogaStyle.alignItems()); yogaStyle.alignSelf() = convertRawProp( - rawProps, "alignSelf", sourceValue.alignSelf(), yogaStyle.alignSelf()); + context, + rawProps, + "alignSelf", + sourceValue.alignSelf(), + yogaStyle.alignSelf()); yogaStyle.positionType() = convertRawProp( + context, rawProps, "position", sourceValue.positionType(), yogaStyle.positionType()); yogaStyle.flexWrap() = convertRawProp( - rawProps, "flexWrap", sourceValue.flexWrap(), yogaStyle.flexWrap()); + context, + rawProps, + "flexWrap", + sourceValue.flexWrap(), + yogaStyle.flexWrap()); yogaStyle.overflow() = convertRawProp( - rawProps, "overflow", sourceValue.overflow(), yogaStyle.overflow()); + context, + rawProps, + "overflow", + sourceValue.overflow(), + yogaStyle.overflow()); yogaStyle.display() = convertRawProp( - rawProps, "display", sourceValue.display(), yogaStyle.display()); - yogaStyle.flex() = - convertRawProp(rawProps, "flex", sourceValue.flex(), yogaStyle.flex()); + context, rawProps, "display", sourceValue.display(), yogaStyle.display()); + yogaStyle.flex() = convertRawProp( + context, rawProps, "flex", sourceValue.flex(), yogaStyle.flex()); yogaStyle.flexGrow() = convertRawProp( - rawProps, "flexGrow", sourceValue.flexGrow(), yogaStyle.flexGrow()); + context, + rawProps, + "flexGrow", + sourceValue.flexGrow(), + yogaStyle.flexGrow()); yogaStyle.flexShrink() = convertRawProp( - rawProps, "flexShrink", sourceValue.flexShrink(), yogaStyle.flexShrink()); + context, + rawProps, + "flexShrink", + sourceValue.flexShrink(), + yogaStyle.flexShrink()); yogaStyle.flexBasis() = convertRawProp( - rawProps, "flexBasis", sourceValue.flexBasis(), yogaStyle.flexBasis()); + context, + rawProps, + "flexBasis", + sourceValue.flexBasis(), + yogaStyle.flexBasis()); yogaStyle.margin() = convertRawProp( - rawProps, "margin", "", sourceValue.margin(), yogaStyle.margin()); - yogaStyle.position() = - convertRawProp(rawProps, sourceValue.position(), yogaStyle.position()); + context, + rawProps, + "margin", + "", + sourceValue.margin(), + yogaStyle.margin()); + yogaStyle.position() = convertRawProp( + context, rawProps, sourceValue.position(), yogaStyle.position()); yogaStyle.padding() = convertRawProp( - rawProps, "padding", "", sourceValue.padding(), yogaStyle.padding()); + context, + rawProps, + "padding", + "", + sourceValue.padding(), + yogaStyle.padding()); yogaStyle.border() = convertRawProp( - rawProps, "border", "Width", sourceValue.border(), yogaStyle.border()); + context, + rawProps, + "border", + "Width", + sourceValue.border(), + yogaStyle.border()); yogaStyle.dimensions() = convertRawProp( + context, rawProps, "width", "height", sourceValue.dimensions(), yogaStyle.dimensions()); yogaStyle.minDimensions() = convertRawProp( + context, rawProps, "minWidth", "minHeight", sourceValue.minDimensions(), yogaStyle.minDimensions()); yogaStyle.maxDimensions() = convertRawProp( + context, rawProps, "maxWidth", "maxHeight", sourceValue.maxDimensions(), yogaStyle.maxDimensions()); yogaStyle.aspectRatio() = convertRawProp( + context, rawProps, "aspectRatio", sourceValue.aspectRatio(), @@ -209,6 +301,7 @@ static inline YGStyle convertRawProp( template static inline CascadedRectangleCorners convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, char const *prefix, char const *suffix, @@ -217,6 +310,7 @@ static inline CascadedRectangleCorners convertRawProp( CascadedRectangleCorners result; result.topLeft = convertRawProp( + context, rawProps, "TopLeft", sourceValue.topLeft, @@ -224,6 +318,7 @@ static inline CascadedRectangleCorners convertRawProp( prefix, suffix); result.topRight = convertRawProp( + context, rawProps, "TopRight", sourceValue.topRight, @@ -231,6 +326,7 @@ static inline CascadedRectangleCorners convertRawProp( prefix, suffix); result.bottomLeft = convertRawProp( + context, rawProps, "BottomLeft", sourceValue.bottomLeft, @@ -238,6 +334,7 @@ static inline CascadedRectangleCorners convertRawProp( prefix, suffix); result.bottomRight = convertRawProp( + context, rawProps, "BottomRight", sourceValue.bottomRight, @@ -246,6 +343,7 @@ static inline CascadedRectangleCorners convertRawProp( suffix); result.topStart = convertRawProp( + context, rawProps, "TopStart", sourceValue.topStart, @@ -253,6 +351,7 @@ static inline CascadedRectangleCorners convertRawProp( prefix, suffix); result.topEnd = convertRawProp( + context, rawProps, "TopEnd", sourceValue.topEnd, @@ -260,6 +359,7 @@ static inline CascadedRectangleCorners convertRawProp( prefix, suffix); result.bottomStart = convertRawProp( + context, rawProps, "BottomStart", sourceValue.bottomStart, @@ -267,6 +367,7 @@ static inline CascadedRectangleCorners convertRawProp( prefix, suffix); result.bottomEnd = convertRawProp( + context, rawProps, "BottomEnd", sourceValue.bottomEnd, @@ -275,13 +376,14 @@ static inline CascadedRectangleCorners convertRawProp( suffix); result.all = convertRawProp( - rawProps, "", sourceValue.all, defaultValue.all, prefix, suffix); + context, rawProps, "", sourceValue.all, defaultValue.all, prefix, suffix); return result; } template static inline CascadedRectangleEdges convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, char const *prefix, char const *suffix, @@ -290,12 +392,31 @@ static inline CascadedRectangleEdges convertRawProp( CascadedRectangleEdges result; result.left = convertRawProp( - rawProps, "Left", sourceValue.left, defaultValue.left, prefix, suffix); + context, + rawProps, + "Left", + sourceValue.left, + defaultValue.left, + prefix, + suffix); result.right = convertRawProp( - rawProps, "Right", sourceValue.right, defaultValue.right, prefix, suffix); + context, + rawProps, + "Right", + sourceValue.right, + defaultValue.right, + prefix, + suffix); result.top = convertRawProp( - rawProps, "Top", sourceValue.top, defaultValue.top, prefix, suffix); + context, + rawProps, + "Top", + sourceValue.top, + defaultValue.top, + prefix, + suffix); result.bottom = convertRawProp( + context, rawProps, "Bottom", sourceValue.bottom, @@ -304,10 +425,23 @@ static inline CascadedRectangleEdges convertRawProp( suffix); result.start = convertRawProp( - rawProps, "Start", sourceValue.start, defaultValue.start, prefix, suffix); + context, + rawProps, + "Start", + sourceValue.start, + defaultValue.start, + prefix, + suffix); result.end = convertRawProp( - rawProps, "End", sourceValue.end, defaultValue.end, prefix, suffix); + context, + rawProps, + "End", + sourceValue.end, + defaultValue.end, + prefix, + suffix); result.horizontal = convertRawProp( + context, rawProps, "Horizontal", sourceValue.horizontal, @@ -315,6 +449,7 @@ static inline CascadedRectangleEdges convertRawProp( prefix, suffix); result.vertical = convertRawProp( + context, rawProps, "Vertical", sourceValue.vertical, @@ -323,7 +458,7 @@ static inline CascadedRectangleEdges convertRawProp( suffix); result.all = convertRawProp( - rawProps, "", sourceValue.all, defaultValue.all, prefix, suffix); + context, rawProps, "", sourceValue.all, defaultValue.all, prefix, suffix); return result; } diff --git a/android/ReactCommon/react/renderer/components/view/tests/ViewTest.cpp b/android/ReactCommon/react/renderer/components/view/tests/ViewTest.cpp index 0b99e92624286..8da0ed46ea6e3 100644 --- a/android/ReactCommon/react/renderer/components/view/tests/ViewTest.cpp +++ b/android/ReactCommon/react/renderer/components/view/tests/ViewTest.cpp @@ -14,7 +14,9 @@ #include #include #include +#include #include + #include #include @@ -62,6 +64,10 @@ class YogaDirtyFlagTest : public ::testing::Test { Element() .reference(scrollViewShadowNode_) .tag(7) + .children({ + Element() + .tag(8) + }) }) }); // clang-format on @@ -81,14 +87,17 @@ class YogaDirtyFlagTest : public ::testing::Test { }; TEST_F(YogaDirtyFlagTest, cloningPropsWithoutChangingThem) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + /* * Cloning props without changing them must *not* dirty a Yoga node. */ auto newRootShadowNode = rootShadowNode_->cloneTree( - innerShadowNode_->getFamily(), [](ShadowNode const &oldShadowNode) { + innerShadowNode_->getFamily(), [&](ShadowNode const &oldShadowNode) { auto &componentDescriptor = oldShadowNode.getComponentDescriptor(); auto props = componentDescriptor.cloneProps( - oldShadowNode.getProps(), RawProps()); + parserContext, oldShadowNode.getProps(), RawProps()); return oldShadowNode.clone(ShadowNodeFragment{props}); }); @@ -208,9 +217,10 @@ TEST_F(YogaDirtyFlagTest, updatingStateForScrollViewMistNotDirtyYogaNode) { oldShadowNode.getFamily(), std::make_shared(state)); - return oldShadowNode.clone({ShadowNodeFragment::propsPlaceholder(), - ShadowNodeFragment::childrenPlaceholder(), - newState}); + return oldShadowNode.clone( + {ShadowNodeFragment::propsPlaceholder(), + ShadowNodeFragment::childrenPlaceholder(), + newState}); }); EXPECT_FALSE( diff --git a/android/ReactCommon/react/renderer/core/Android.mk b/android/ReactCommon/react/renderer/core/Android.mk index 66912423b0514..32a8ddf5efe2c 100644 --- a/android/ReactCommon/react/renderer/core/Android.mk +++ b/android/ReactCommon/react/renderer/core/Android.mk @@ -15,17 +15,19 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ -LOCAL_SHARED_LIBRARIES := libfolly_json libjsi libfolly_futures libreact_utils libreact_render_debug libreact_render_graphics +LOCAL_SHARED_LIBRARIES := libfolly_json libjsi libfolly_futures libreact_utils libreact_debug libreact_render_debug libreact_render_graphics libglog LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) $(call import-module,folly) $(call import-module,jsi) $(call import-module,react/utils) +$(call import-module,react/debug) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/renderer/core/BUCK b/android/ReactCommon/react/renderer/core/BUCK index 13fa6dd9f1dae..feb8f6569be01 100644 --- a/android/ReactCommon/react/renderer/core/BUCK +++ b/android/ReactCommon/react/renderer/core/BUCK @@ -1,5 +1,4 @@ load("@fbsource//tools/build_defs:fb_xplat_cxx_binary.bzl", "fb_xplat_cxx_binary") -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -32,12 +32,8 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/core", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], + compiler_flags_enable_exceptions = True, + compiler_flags_enable_rtti = True, # Needed for DebugStringConvertible - need to find a non-RTTI way to do this / enable RTTI for debug builds only fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -58,6 +54,7 @@ rn_xplat_cxx_library( "//xplat/folly:molly", "//xplat/jsi:JSIDynamic", "//xplat/jsi:jsi", + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/utils:utils"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/graphics:graphics"), @@ -71,7 +68,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], @@ -79,12 +76,14 @@ fb_xplat_cxx_test( deps = [ "//xplat/folly:molly", "//xplat/js/react-native-github/ReactCommon/react/renderer/element:element", + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/components/view:view"), react_native_xplat_target("react/renderer/components/scrollview:scrollview"), react_native_xplat_target("react/renderer/components/text:text"), "//xplat/third-party/gmock:gtest", react_native_xplat_target("react/renderer/components/view:view"), ":core", + "//xplat/hermes/API:HermesAPI", ], ) @@ -94,7 +93,7 @@ fb_xplat_cxx_binary( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", "-Wno-unused-variable", ], diff --git a/android/ReactCommon/react/renderer/core/BatchedEventQueue.cpp b/android/ReactCommon/react/renderer/core/BatchedEventQueue.cpp index d8acdd6112b4c..64d8df3288ce7 100644 --- a/android/ReactCommon/react/renderer/core/BatchedEventQueue.cpp +++ b/android/ReactCommon/react/renderer/core/BatchedEventQueue.cpp @@ -6,33 +6,17 @@ */ #include "BatchedEventQueue.h" -#include namespace facebook { namespace react { -void BatchedEventQueue::onEnqueue() const { - EventQueue::onEnqueue(); +BatchedEventQueue::BatchedEventQueue( + EventQueueProcessor eventProcessor, + std::unique_ptr eventBeat) + : EventQueue(std::move(eventProcessor), std::move(eventBeat)) {} +void BatchedEventQueue::onEnqueue() const { eventBeat_->request(); } - -void BatchedEventQueue::enqueueUniqueEvent(const RawEvent &rawEvent) const { - { - std::lock_guard lock(queueMutex_); - if (!eventQueue_.empty()) { - auto const position = eventQueue_.back(); - if (position.type == rawEvent.type && - position.eventTarget == rawEvent.eventTarget) { - eventQueue_.pop_back(); - } - } - - eventQueue_.push_back(rawEvent); - } - - onEnqueue(); -} - } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/BatchedEventQueue.h b/android/ReactCommon/react/renderer/core/BatchedEventQueue.h index 2acabd8600b74..ea18dcf3d928b 100644 --- a/android/ReactCommon/react/renderer/core/BatchedEventQueue.h +++ b/android/ReactCommon/react/renderer/core/BatchedEventQueue.h @@ -8,6 +8,7 @@ #pragma once #include +#include namespace facebook { namespace react { @@ -18,16 +19,11 @@ namespace react { */ class BatchedEventQueue final : public EventQueue { public: - using EventQueue::EventQueue; + BatchedEventQueue( + EventQueueProcessor eventProcessor, + std::unique_ptr eventBeat); void onEnqueue() const override; - - /* - * Enqueues and (probably later) dispatch a given event. - * Deletes last RawEvent from the queu if it has the same type and target. - * Can be called on any thread. - */ - void enqueueUniqueEvent(const RawEvent &rawEvent) const; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/core/ComponentDescriptor.h b/android/ReactCommon/react/renderer/core/ComponentDescriptor.h index 04ad7f27bb565..e0bac0845eadc 100644 --- a/android/ReactCommon/react/renderer/core/ComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/core/ComponentDescriptor.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -40,8 +41,8 @@ class ComponentDescriptor { * and `ComponentHandle` (the particular custom implementation might use * stored `flavor` to return different values from those virtual methods). * Since it's a very niche requirement (e.g. we plan to use it for - * an interoperability layer with Paper), we are thinking about removing this - * feature completely after it's no longer needed. + * an interoperability layer with old renderer), we are thinking about + * removing this feature completely after it's no longer needed. */ using Flavor = std::shared_ptr; @@ -82,7 +83,7 @@ class ComponentDescriptor { /* * Clones a `ShadowNode` with optionally new `props` and/or `children`. */ - virtual UnsharedShadowNode cloneShadowNode( + virtual ShadowNode::Unshared cloneShadowNode( const ShadowNode &sourceShadowNode, const ShadowNodeFragment &fragment) const = 0; @@ -101,6 +102,7 @@ class ComponentDescriptor { * Must return an object which is NOT pointer equal to `props`. */ virtual SharedProps cloneProps( + const PropsParserContext &context, const SharedProps &props, const RawProps &rawProps) const = 0; @@ -109,6 +111,7 @@ class ComponentDescriptor { * between `props` and `newProps`. */ virtual SharedProps interpolateProps( + const PropsParserContext &context, float animationProgress, const SharedProps &props, const SharedProps &newProps) const = 0; diff --git a/android/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h b/android/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h index 497cf561e3d8c..08193e7609f32 100644 --- a/android/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h @@ -10,9 +10,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -63,8 +66,6 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { ShadowNode::Shared createShadowNode( const ShadowNodeFragment &fragment, ShadowNodeFamily::Shared const &family) const override { - assert(std::dynamic_pointer_cast(fragment.props)); - auto shadowNode = std::make_shared(fragment, family, getTraits()); @@ -73,13 +74,9 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { return shadowNode; } - UnsharedShadowNode cloneShadowNode( + ShadowNode::Unshared cloneShadowNode( const ShadowNode &sourceShadowNode, const ShadowNodeFragment &fragment) const override { - assert( - dynamic_cast(&sourceShadowNode) && - "Provided `sourceShadowNode` has an incompatible type."); - auto shadowNode = std::make_shared(sourceShadowNode, fragment); adopt(shadowNode); @@ -89,10 +86,6 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { void appendChild( const ShadowNode::Shared &parentShadowNode, const ShadowNode::Shared &childShadowNode) const override { - assert( - dynamic_cast(parentShadowNode.get()) && - "Provided `parentShadowNode` has an incompatible type."); - auto concreteParentShadowNode = std::static_pointer_cast(parentShadowNode); auto concreteNonConstParentShadowNode = @@ -101,13 +94,9 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { } virtual SharedProps cloneProps( + const PropsParserContext &context, const SharedProps &props, const RawProps &rawProps) const override { - assert( - !props || - dynamic_cast(props.get()) && - "Provided `props` has an incompatible type."); - // Optimization: // Quite often nodes are constructed with default/empty props: the base // `props` object is `null` (there no base because it's not cloning) and the @@ -117,17 +106,33 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { return ShadowNodeT::defaultSharedProps(); } - rawProps.parse(rawPropsParser_); + rawProps.parse(rawPropsParser_, context); - return ShadowNodeT::Props(rawProps, props); + return ShadowNodeT::Props(context, rawProps, props); }; - virtual SharedProps interpolateProps( + SharedProps interpolateProps( + const PropsParserContext &context, float animationProgress, const SharedProps &props, const SharedProps &newProps) const override { - // By default, this does nothing. - return cloneProps(newProps, {}); +#ifdef ANDROID + // On Android only, the merged props should have the same RawProps as the + // final props struct + SharedProps interpolatedPropsShared = + (newProps != nullptr ? cloneProps(context, newProps, newProps->rawProps) + : cloneProps(context, newProps, {})); +#else + SharedProps interpolatedPropsShared = cloneProps(context, newProps, {}); +#endif + + if (ConcreteShadowNode::BaseTraits().check( + ShadowNodeTraits::Trait::ViewKind)) { + interpolateViewProps( + animationProgress, props, newProps, interpolatedPropsShared); + } + + return interpolatedPropsShared; }; virtual State::Shared createInitialState( @@ -153,14 +158,14 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { return nullptr; } - assert(data && "Provided `data` is nullptr."); + react_native_assert(data && "Provided `data` is nullptr."); return std::make_shared( std::static_pointer_cast(data), *family.getMostRecentState()); } - virtual ShadowNodeFamily::Shared createFamily( + ShadowNodeFamily::Shared createFamily( ShadowNodeFamilyFragment const &fragment, SharedEventTarget eventTarget) const override { auto eventEmitter = std::make_shared( @@ -173,9 +178,10 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { } protected: - virtual void adopt(UnsharedShadowNode shadowNode) const { + virtual void adopt(ShadowNode::Unshared const &shadowNode) const { // Default implementation does nothing. - assert(shadowNode->getComponentHandle() == getComponentHandle()); + react_native_assert( + shadowNode->getComponentHandle() == getComponentHandle()); } }; diff --git a/android/ReactCommon/react/renderer/core/ConcreteShadowNode.h b/android/ReactCommon/react/renderer/core/ConcreteShadowNode.h index e6b842f3c820b..537aa6dcbf205 100644 --- a/android/ReactCommon/react/renderer/core/ConcreteShadowNode.h +++ b/android/ReactCommon/react/renderer/core/ConcreteShadowNode.h @@ -7,9 +7,11 @@ #pragma once +#include #include -#include #include +#include +#include #include #include @@ -49,7 +51,6 @@ class ConcreteShadowNode : public BaseShadowNodeT { using SharedConcreteEventEmitter = std::shared_ptr; using SharedConcreteShadowNode = std::shared_ptr; using ConcreteState = ConcreteState; - using ConcreteStateTeller = ConcreteStateTeller; using ConcreteStateData = StateDataT; static ComponentName Name() { @@ -69,9 +70,11 @@ class ConcreteShadowNode : public BaseShadowNodeT { } static SharedConcreteProps Props( + const PropsParserContext &context, RawProps const &rawProps, SharedProps const &baseProps = nullptr) { return std::make_shared( + context, baseProps ? static_cast(*baseProps) : PropsT(), rawProps); } @@ -94,10 +97,8 @@ class ConcreteShadowNode : public BaseShadowNodeT { * Thread-safe after the node is sealed. */ ConcreteProps const &getConcreteProps() const { - assert(BaseShadowNodeT::props_ && "Props must not be `nullptr`."); - assert( - std::dynamic_pointer_cast(props_) && - "Props must be an instance of ConcreteProps class."); + react_native_assert( + BaseShadowNodeT::props_ && "Props must not be `nullptr`."); return static_cast(*props_); } @@ -106,10 +107,6 @@ class ConcreteShadowNode : public BaseShadowNodeT { * Thread-safe after the node is sealed. */ ConcreteEventEmitter const &getConcreteEventEmitter() const { - assert( - std::dynamic_pointer_cast( - BaseShadowNodeT::getEventEmitter()) && - "EventEmitter must be an instance of ConcreteEventEmitter class."); return static_cast( *BaseShadowNodeT::getEventEmitter()); } @@ -119,10 +116,7 @@ class ConcreteShadowNode : public BaseShadowNodeT { * Thread-safe after the node is sealed. */ ConcreteStateData const &getStateData() const { - assert(state_ && "State must not be `nullptr`."); - assert( - std::dynamic_pointer_cast(state_) && - "State must be an instance of ConcreteState class."); + react_native_assert(state_ && "State must not be `nullptr`."); return static_cast(state_.get())->getData(); } diff --git a/android/ReactCommon/react/renderer/core/ConcreteState.h b/android/ReactCommon/react/renderer/core/ConcreteState.h index 33fa5dac78839..c4f8bef37e1d2 100644 --- a/android/ReactCommon/react/renderer/core/ConcreteState.h +++ b/android/ReactCommon/react/renderer/core/ConcreteState.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace facebook { @@ -60,55 +61,23 @@ class ConcreteState : public State { */ void updateState( Data &&newData, - std::function failureCallback = nullptr, EventPriority priority = EventPriority::AsynchronousUnbatched) const { updateState( - [data = std::move(newData)](Data const &oldData) mutable -> Data && { - return std::move(data); + [data{std::move(newData)}](Data const &oldData) -> SharedData { + return std::make_shared(data); }, - failureCallback, priority); } /* * Initiate a state update process with a given function (that transforms an - * old data value to a new one) and priority. The update function can be - * called from any thread any moment later. The function can be called only - * once or not called at all (in the case where the node was already unmounted - * and updating makes no sense). The state update operation might fail in case - * of conflict. + * old data value to a new one) and priority. The callback function can be + * called from any thread any moment later. + * In case of a conflict, the `callback` might be called several times until + * it succeeded. To cancel the state update operation, the callback needs to + * return `nullptr`. */ void updateState( - std::function callback, - std::function failureCallback = nullptr, - EventPriority priority = EventPriority::AsynchronousBatched) const { - auto family = family_.lock(); - - if (!family) { - // No more nodes of this family exist anymore, - // updating state is impossible. - return; - } - - auto stateUpdate = StateUpdate{ - family, - [=](StateData::Shared const &oldData) -> StateData::Shared { - assert(oldData); - return std::make_shared( - callback(*std::static_pointer_cast(oldData))); - }, - failureCallback, - false}; - - family->dispatchRawState(std::move(stateUpdate), priority); - } - - /* - * An experimental version of `updateState` function that re-commit the state - * update over and over again until it succeeded. To cancel the state update - * operation, the state update lambda needs to return `nullptr`. - */ - void updateStateWithAutorepeat( std::function callback, EventPriority priority = EventPriority::AsynchronousBatched) const { auto family = family_.lock(); @@ -120,14 +89,10 @@ class ConcreteState : public State { } auto stateUpdate = StateUpdate{ - family, - [=](StateData::Shared const &oldData) -> StateData::Shared { - assert(oldData); + family, [=](StateData::Shared const &oldData) -> StateData::Shared { + react_native_assert(oldData); return callback(*std::static_pointer_cast(oldData)); - }, - nullptr, - true, - }; + }}; family->dispatchRawState(std::move(stateUpdate), priority); } @@ -137,9 +102,11 @@ class ConcreteState : public State { return getData().getDynamic(); } - void updateState(folly::dynamic data, std::function failureCallback) - const override { - updateState(std::move(Data(getData(), data)), failureCallback); + void updateState(folly::dynamic data) const override { + updateState(std::move(Data(getData(), data))); + } + MapBuffer getMapBuffer() const override { + return getData().getMapBuffer(); } #endif }; diff --git a/android/ReactCommon/react/renderer/core/ConcreteStateTeller.h b/android/ReactCommon/react/renderer/core/ConcreteStateTeller.h deleted file mode 100644 index 2354ae0daaf7e..0000000000000 --- a/android/ReactCommon/react/renderer/core/ConcreteStateTeller.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -namespace facebook { -namespace react { - -/* - * Wrapper for `ConreteState` class designed to make interactions with - * ConcreteState easier. - */ -template -class ConcreteStateTeller { - public: - using Data = typename ConcreteStateT::Data; - - /* - * Sets backing `ConcreteState` on which all the methods will be called. - * Can be called from any thread. - */ - void setConcreteState(State::Shared const &state) { - std::lock_guard lock(mutex_); - concreteState_ = std::static_pointer_cast(state); - } - - /* - * Removes reference to `ConcreteState` previously set in `setConcreteState`. - * Can be called from any thread. - */ - void invalidate() { - std::lock_guard lock(mutex_); - concreteState_ = nullptr; - } - - /* - * Returns data if state isn't nullptr. - * Can be called from any thread. - */ - better::optional getData() const { - std::lock_guard lock(mutex_); - if (concreteState_) { - return concreteState_->getData(); - } else { - return {}; - } - } - - /* - * Returns true if backing state isn't nullptr, false otherwise. - * Can be called from any thread. - */ - bool isValid() const { - std::lock_guard lock(mutex_); - return concreteState_ != nullptr; - } - - /* - * Initiate a state update process with given new data and priority. - * This is a simplified convenience version of the method that receives a - * function for cases where a new value of data does not depend on an old - * value. - */ - void updateState( - Data &&newData, - EventPriority priority = EventPriority::AsynchronousUnbatched) const { - updateState( - [data = std::move(newData)](Data const &oldData) -> Data { - return std::move(data); - }, - priority); - } - - /* - * Initiate a state update process with a given function (that transforms an - * old data value to a new one) and priority. The update function can be - * called from any thread any moment later. The function can be called only - * once or not called at all (in the case where the node was already unmounted - * and updating makes no sense). The state update operation might fail in case - * of conflict. - */ - void updateState( - std::function callback, - EventPriority priority = EventPriority::AsynchronousBatched) const { - std::shared_ptr concreteState; - { - std::lock_guard lock(mutex_); - if (!concreteState_) { - return; - } - concreteState = concreteState_; - } - - concreteState->updateState( - callback, - [=]() { - updateStateRetryIfNecesarry_(concreteState, callback, priority, 1); - }, - priority); - } - - private: - /* - * Protected by `mutex_`. - */ - std::shared_ptr concreteState_; - - /* - * Protects `concreteState_`. - */ - std::mutex mutable mutex_; - - void updateStateRetryIfNecesarry_( - std::shared_ptr concreteState, - std::function callback, - EventPriority priority, - int retryCount) const { - { - std::lock_guard lock(mutex_); - - if (concreteState != concreteState_) { - LOG(WARNING) << "ConcreteState_ changed while retrying"; - return; - } - } - - if (retryCount > 60) { - LOG(ERROR) << "Exceeded 60 retries"; - assert(false); - return; - } - - concreteState->updateState( - callback, - [=] { - updateStateRetryIfNecesarry_( - concreteState, callback, priority, retryCount + 1); - }, - priority); - } -}; - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/DynamicPropsUtilities.cpp b/android/ReactCommon/react/renderer/core/DynamicPropsUtilities.cpp new file mode 100644 index 0000000000000..428ae4f3b8307 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/DynamicPropsUtilities.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "DynamicPropsUtilities.h" + +namespace facebook { +namespace react { +folly::dynamic mergeDynamicProps( + folly::dynamic const &source, + folly::dynamic const &patch) { + auto result = source; + + if (!result.isObject()) { + result = folly::dynamic::object(); + } + + if (!patch.isObject()) { + return result; + } + + // Note, here we have to preserve sub-prop objects with `null` value as + // an indication for the legacy mounting layer that it needs to clean them up. + for (auto const &pair : patch.items()) { + result[pair.first] = pair.second; + } + + return result; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerState.h b/android/ReactCommon/react/renderer/core/DynamicPropsUtilities.h similarity index 68% rename from android/ReactCommon/react/renderer/components/picker/iospicker/PickerState.h rename to android/ReactCommon/react/renderer/core/DynamicPropsUtilities.h index fa50b38dfdbf6..9e7283cb8fc33 100644 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerState.h +++ b/android/ReactCommon/react/renderer/core/DynamicPropsUtilities.h @@ -7,13 +7,14 @@ #pragma once +#include + namespace facebook { namespace react { -/* - * State for component. - */ -class PickerState final {}; +folly::dynamic mergeDynamicProps( + folly::dynamic const &source, + folly::dynamic const &patch); } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/EventDispatcher.cpp b/android/ReactCommon/react/renderer/core/EventDispatcher.cpp index 8ece2ab3a07af..3cfe35f3e0cf6 100644 --- a/android/ReactCommon/react/renderer/core/EventDispatcher.cpp +++ b/android/ReactCommon/react/renderer/core/EventDispatcher.cpp @@ -17,31 +17,25 @@ namespace facebook { namespace react { EventDispatcher::EventDispatcher( - EventPipe const &eventPipe, - StatePipe const &statePipe, + EventQueueProcessor eventProcessor, EventBeat::Factory const &synchonousEventBeatFactory, EventBeat::Factory const &asynchonousEventBeatFactory, EventBeat::SharedOwnerBox const &ownerBox) : synchronousUnbatchedQueue_(std::make_unique( - eventPipe, - statePipe, + eventProcessor, synchonousEventBeatFactory(ownerBox))), synchronousBatchedQueue_(std::make_unique( - eventPipe, - statePipe, + eventProcessor, synchonousEventBeatFactory(ownerBox))), asynchronousUnbatchedQueue_(std::make_unique( - eventPipe, - statePipe, + eventProcessor, asynchonousEventBeatFactory(ownerBox))), asynchronousBatchedQueue_(std::make_unique( - eventPipe, - statePipe, + eventProcessor, asynchonousEventBeatFactory(ownerBox))) {} -void EventDispatcher::dispatchEvent( - RawEvent const &rawEvent, - EventPriority priority) const { +void EventDispatcher::dispatchEvent(RawEvent &&rawEvent, EventPriority priority) + const { getEventQueue(priority).enqueueEvent(std::move(rawEvent)); } @@ -51,8 +45,8 @@ void EventDispatcher::dispatchStateUpdate( getEventQueue(priority).enqueueStateUpdate(std::move(stateUpdate)); } -void EventDispatcher::dispatchUniqueEvent(RawEvent const &rawEvent) const { - asynchronousBatchedQueue_->enqueueUniqueEvent(rawEvent); +void EventDispatcher::dispatchUniqueEvent(RawEvent &&rawEvent) const { + asynchronousBatchedQueue_->enqueueUniqueEvent(std::move(rawEvent)); } const EventQueue &EventDispatcher::getEventQueue(EventPriority priority) const { diff --git a/android/ReactCommon/react/renderer/core/EventDispatcher.h b/android/ReactCommon/react/renderer/core/EventDispatcher.h index d3fb37c132754..f81ccd7cc2686 100644 --- a/android/ReactCommon/react/renderer/core/EventDispatcher.h +++ b/android/ReactCommon/react/renderer/core/EventDispatcher.h @@ -7,21 +7,17 @@ #pragma once -#include -#include - #include #include -#include #include -#include +#include #include #include namespace facebook { namespace react { -class RawEvent; +struct RawEvent; /* * Represents event-delivery infrastructure. @@ -33,8 +29,7 @@ class EventDispatcher { using Weak = std::weak_ptr; EventDispatcher( - EventPipe const &eventPipe, - StatePipe const &statePipe, + EventQueueProcessor eventProcessor, EventBeat::Factory const &synchonousEventBeatFactory, EventBeat::Factory const &asynchonousEventBeatFactory, EventBeat::SharedOwnerBox const &ownerBox); @@ -42,14 +37,14 @@ class EventDispatcher { /* * Dispatches a raw event with given priority using event-delivery pipe. */ - void dispatchEvent(RawEvent const &rawEvent, EventPriority priority) const; + void dispatchEvent(RawEvent &&rawEvent, EventPriority priority) const; /* * Dispatches a raw event with asynchronous batched priority. Before the * dispatch we make sure that no other RawEvent of same type and same target * is on the queue. */ - void dispatchUniqueEvent(RawEvent const &rawEvent) const; + void dispatchUniqueEvent(RawEvent &&rawEvent) const; /* * Dispatches a state update with given priority. diff --git a/android/ReactCommon/react/renderer/core/EventEmitter.cpp b/android/ReactCommon/react/renderer/core/EventEmitter.cpp index 54a0c8154b2ac..cbc4dfd29b82f 100644 --- a/android/ReactCommon/react/renderer/core/EventEmitter.cpp +++ b/android/ReactCommon/react/renderer/core/EventEmitter.cpp @@ -26,7 +26,7 @@ static std::string normalizeEventType(const std::string &type) { auto prefixedType = type; if (type.find("top", 0) != 0) { prefixedType.insert(0, "top"); - prefixedType[3] = toupper(prefixedType[3]); + prefixedType[3] = static_cast(toupper(prefixedType[3])); } return prefixedType; } @@ -52,19 +52,30 @@ EventEmitter::EventEmitter( void EventEmitter::dispatchEvent( const std::string &type, const folly::dynamic &payload, - const EventPriority &priority) const { + EventPriority priority, + RawEvent::Category category) const { dispatchEvent( type, [payload](jsi::Runtime &runtime) { return valueFromDynamic(runtime, payload); }, - priority); + priority, + category); +} + +void EventEmitter::dispatchUniqueEvent( + const std::string &type, + const folly::dynamic &payload) const { + dispatchUniqueEvent(type, [payload](jsi::Runtime &runtime) { + return valueFromDynamic(runtime, payload); + }); } void EventEmitter::dispatchEvent( const std::string &type, const ValueFactory &payloadFactory, - const EventPriority &priority) const { + EventPriority priority, + RawEvent::Category category) const { SystraceSection s("EventEmitter::dispatchEvent"); auto eventDispatcher = eventDispatcher_.lock(); @@ -73,7 +84,8 @@ void EventEmitter::dispatchEvent( } eventDispatcher->dispatchEvent( - RawEvent(normalizeEventType(type), payloadFactory, eventTarget_), + RawEvent( + normalizeEventType(type), payloadFactory, eventTarget_, category), priority); } @@ -87,8 +99,11 @@ void EventEmitter::dispatchUniqueEvent( return; } - eventDispatcher->dispatchUniqueEvent( - RawEvent(normalizeEventType(type), payloadFactory, eventTarget_)); + eventDispatcher->dispatchUniqueEvent(RawEvent( + normalizeEventType(type), + payloadFactory, + eventTarget_, + RawEvent::Category::Continuous)); } void EventEmitter::setEnabled(bool enabled) const { diff --git a/android/ReactCommon/react/renderer/core/EventEmitter.h b/android/ReactCommon/react/renderer/core/EventEmitter.h index 55bdf8f040158..f2f484b0d398c 100644 --- a/android/ReactCommon/react/renderer/core/EventEmitter.h +++ b/android/ReactCommon/react/renderer/core/EventEmitter.h @@ -71,12 +71,18 @@ class EventEmitter { const std::string &type, const ValueFactory &payloadFactory = EventEmitter::defaultPayloadFactory(), - const EventPriority &priority = EventPriority::AsynchronousBatched) const; + EventPriority priority = EventPriority::AsynchronousBatched, + RawEvent::Category category = RawEvent::Category::Unspecified) const; void dispatchEvent( const std::string &type, const folly::dynamic &payload, - const EventPriority &priority = EventPriority::AsynchronousBatched) const; + EventPriority priority = EventPriority::AsynchronousBatched, + RawEvent::Category category = RawEvent::Category::Unspecified) const; + + void dispatchUniqueEvent( + const std::string &type, + const folly::dynamic &payload) const; void dispatchUniqueEvent( const std::string &type, diff --git a/android/ReactCommon/react/renderer/core/EventPipe.h b/android/ReactCommon/react/renderer/core/EventPipe.h index b0264579ae040..3e9ac127e6bb0 100644 --- a/android/ReactCommon/react/renderer/core/EventPipe.h +++ b/android/ReactCommon/react/renderer/core/EventPipe.h @@ -12,6 +12,7 @@ #include #include +#include #include namespace facebook { @@ -21,6 +22,7 @@ using EventPipe = std::function; } // namespace react diff --git a/android/ReactCommon/react/renderer/core/EventPriority.h b/android/ReactCommon/react/renderer/core/EventPriority.h index 61e3ad39dbdf5..8611f105800e0 100644 --- a/android/ReactCommon/react/renderer/core/EventPriority.h +++ b/android/ReactCommon/react/renderer/core/EventPriority.h @@ -10,7 +10,7 @@ namespace facebook { namespace react { -enum class EventPriority : int { +enum class EventPriority { SynchronousUnbatched, SynchronousBatched, AsynchronousUnbatched, diff --git a/android/ReactCommon/react/renderer/core/EventQueue.cpp b/android/ReactCommon/react/renderer/core/EventQueue.cpp index 2db952292ff9e..4d10e4c74423a 100644 --- a/android/ReactCommon/react/renderer/core/EventQueue.cpp +++ b/android/ReactCommon/react/renderer/core/EventQueue.cpp @@ -14,26 +14,54 @@ namespace facebook { namespace react { EventQueue::EventQueue( - EventPipe eventPipe, - StatePipe statePipe, + EventQueueProcessor eventProcessor, std::unique_ptr eventBeat) - : eventPipe_(std::move(eventPipe)), - statePipe_(std::move(statePipe)), + : eventProcessor_(std::move(eventProcessor)), eventBeat_(std::move(eventBeat)) { eventBeat_->setBeatCallback( std::bind(&EventQueue::onBeat, this, std::placeholders::_1)); } -void EventQueue::enqueueEvent(const RawEvent &rawEvent) const { +void EventQueue::enqueueEvent(RawEvent &&rawEvent) const { { std::lock_guard lock(queueMutex_); - eventQueue_.push_back(rawEvent); + eventQueue_.push_back(std::move(rawEvent)); } onEnqueue(); } -void EventQueue::enqueueStateUpdate(const StateUpdate &stateUpdate) const { +void EventQueue::enqueueUniqueEvent(RawEvent &&rawEvent) const { + { + std::lock_guard lock(queueMutex_); + + auto repeatedEvent = eventQueue_.rend(); + + for (auto it = eventQueue_.rbegin(); it != eventQueue_.rend(); ++it) { + if (it->type == rawEvent.type && + it->eventTarget == rawEvent.eventTarget) { + repeatedEvent = it; + break; + } else if (it->eventTarget == rawEvent.eventTarget) { + // It is necessary to maintain order of different event types + // for the same target. If the same target has event types A1, B1 + // in the event queue and event A2 occurs. A1 has to stay in the + // queue. + break; + } + } + + if (repeatedEvent == eventQueue_.rend()) { + eventQueue_.push_back(std::move(rawEvent)); + } else { + *repeatedEvent = std::move(rawEvent); + } + } + + onEnqueue(); +} + +void EventQueue::enqueueStateUpdate(StateUpdate &&stateUpdate) const { { std::lock_guard lock(queueMutex_); if (!stateUpdateQueue_.empty()) { @@ -42,19 +70,15 @@ void EventQueue::enqueueStateUpdate(const StateUpdate &stateUpdate) const { stateUpdateQueue_.pop_back(); } } - stateUpdateQueue_.push_back(stateUpdate); + stateUpdateQueue_.push_back(std::move(stateUpdate)); } onEnqueue(); } -void EventQueue::onEnqueue() const { - // Default implementation does nothing. -} - void EventQueue::onBeat(jsi::Runtime &runtime) const { - flushEvents(runtime); flushStateUpdates(); + flushEvents(runtime); } void EventQueue::flushEvents(jsi::Runtime &runtime) const { @@ -71,30 +95,7 @@ void EventQueue::flushEvents(jsi::Runtime &runtime) const { eventQueue_.clear(); } - { - std::lock_guard lock(EventEmitter::DispatchMutex()); - - for (const auto &event : queue) { - if (event.eventTarget) { - event.eventTarget->retain(runtime); - } - } - } - - for (const auto &event : queue) { - eventPipe_( - runtime, event.eventTarget.get(), event.type, event.payloadFactory); - } - - // No need to lock `EventEmitter::DispatchMutex()` here. - // The mutex protects from a situation when the `instanceHandle` can be - // deallocated during accessing, but that's impossible at this point because - // we have a strong pointer to it. - for (const auto &event : queue) { - if (event.eventTarget) { - event.eventTarget->release(runtime); - } - } + eventProcessor_.flushEvents(runtime, std::move(queue)); } void EventQueue::flushStateUpdates() const { @@ -111,9 +112,7 @@ void EventQueue::flushStateUpdates() const { stateUpdateQueue_.clear(); } - for (const auto &stateUpdate : stateUpdateQueue) { - statePipe_(stateUpdate); - } + eventProcessor_.flushStateUpdates(std::move(stateUpdateQueue)); } } // namespace react diff --git a/android/ReactCommon/react/renderer/core/EventQueue.h b/android/ReactCommon/react/renderer/core/EventQueue.h index ab6bac9ac3bbf..d0f9da84426df 100644 --- a/android/ReactCommon/react/renderer/core/EventQueue.h +++ b/android/ReactCommon/react/renderer/core/EventQueue.h @@ -13,9 +13,8 @@ #include #include -#include +#include #include -#include #include namespace facebook { @@ -28,8 +27,7 @@ namespace react { class EventQueue { public: EventQueue( - EventPipe eventPipe, - StatePipe statePipe, + EventQueueProcessor eventProcessor, std::unique_ptr eventBeat); virtual ~EventQueue() = default; @@ -37,13 +35,20 @@ class EventQueue { * Enqueues and (probably later) dispatch a given event. * Can be called on any thread. */ - void enqueueEvent(const RawEvent &rawEvent) const; + void enqueueEvent(RawEvent &&rawEvent) const; + + /* + * Enqueues and (probably later) dispatches a given event. + * Deletes last RawEvent from the queue if it has the same type and target. + * Can be called on any thread. + */ + void enqueueUniqueEvent(RawEvent &&rawEvent) const; /* * Enqueues and (probably later) dispatch a given state update. * Can be called on any thread. */ - void enqueueStateUpdate(const StateUpdate &stateUpdate) const; + void enqueueStateUpdate(StateUpdate &&stateUpdate) const; protected: /* @@ -51,19 +56,20 @@ class EventQueue { * Override in subclasses to trigger beat `request` and/or beat `induce`. * Default implementation does nothing. */ - virtual void onEnqueue() const; + virtual void onEnqueue() const = 0; void onBeat(jsi::Runtime &runtime) const; void flushEvents(jsi::Runtime &runtime) const; void flushStateUpdates() const; - const EventPipe eventPipe_; - const StatePipe statePipe_; + EventQueueProcessor eventProcessor_; + const std::unique_ptr eventBeat_; // Thread-safe, protected by `queueMutex_`. mutable std::vector eventQueue_; mutable std::vector stateUpdateQueue_; mutable std::mutex queueMutex_; + mutable bool hasContinuousEventStarted_{false}; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/core/EventQueueProcessor.cpp b/android/ReactCommon/react/renderer/core/EventQueueProcessor.cpp new file mode 100644 index 0000000000000..40b813e155aa9 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/EventQueueProcessor.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "EventQueue.h" + +#include "EventEmitter.h" +#include "ShadowNodeFamily.h" + +namespace facebook { +namespace react { + +EventQueueProcessor::EventQueueProcessor( + EventPipe eventPipe, + StatePipe statePipe) + : eventPipe_(std::move(eventPipe)), statePipe_(std::move(statePipe)) {} + +void EventQueueProcessor::flushEvents( + jsi::Runtime &runtime, + std::vector &&events) const { + { + std::lock_guard lock(EventEmitter::DispatchMutex()); + + for (const auto &event : events) { + if (event.eventTarget) { + event.eventTarget->retain(runtime); + } + } + } + + for (auto const &event : events) { + if (event.category == RawEvent::Category::ContinuousEnd) { + hasContinuousEventStarted_ = false; + } + + auto reactPriority = hasContinuousEventStarted_ + ? ReactEventPriority::Default + : ReactEventPriority::Discrete; + + if (event.category == RawEvent::Category::Continuous) { + reactPriority = ReactEventPriority::Default; + } + + if (event.category == RawEvent::Category::Discrete) { + reactPriority = ReactEventPriority::Discrete; + } + + eventPipe_( + runtime, + event.eventTarget.get(), + event.type, + reactPriority, + event.payloadFactory); + + if (event.category == RawEvent::Category::ContinuousStart) { + hasContinuousEventStarted_ = true; + } + } + + // No need to lock `EventEmitter::DispatchMutex()` here. + // The mutex protects from a situation when the `instanceHandle` can be + // deallocated during accessing, but that's impossible at this point because + // we have a strong pointer to it. + for (const auto &event : events) { + if (event.eventTarget) { + event.eventTarget->release(runtime); + } + } +} + +void EventQueueProcessor::flushStateUpdates( + std::vector &&states) const { + for (const auto &stateUpdate : states) { + statePipe_(stateUpdate); + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/EventQueueProcessor.h b/android/ReactCommon/react/renderer/core/EventQueueProcessor.h new file mode 100644 index 0000000000000..933cc9a207961 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/EventQueueProcessor.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class EventQueueProcessor { + public: + EventQueueProcessor(EventPipe eventPipe, StatePipe statePipe); + + void flushEvents(jsi::Runtime &runtime, std::vector &&events) const; + void flushStateUpdates(std::vector &&states) const; + + private: + EventPipe const eventPipe_; + StatePipe const statePipe_; + + mutable bool hasContinuousEventStarted_{false}; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/EventTarget.cpp b/android/ReactCommon/react/renderer/core/EventTarget.cpp index 01a5a77bd50f4..b8d84547016ad 100644 --- a/android/ReactCommon/react/renderer/core/EventTarget.cpp +++ b/android/ReactCommon/react/renderer/core/EventTarget.cpp @@ -7,6 +7,8 @@ #include "EventTarget.h" +#include + namespace facebook { namespace react { @@ -38,8 +40,8 @@ void EventTarget::retain(jsi::Runtime &runtime) const { // particular implementation of JSI was able to detect this inconsistency and // dealt with it, but some JSI implementation may not support this feature and // that case will lead to a crash in those environments. - assert(!strongInstanceHandle_.isNull()); - assert(!strongInstanceHandle_.isUndefined()); + react_native_assert(!strongInstanceHandle_.isNull()); + react_native_assert(!strongInstanceHandle_.isUndefined()); } void EventTarget::release(jsi::Runtime &runtime) const { diff --git a/android/ReactCommon/react/renderer/core/LayoutConstraints.h b/android/ReactCommon/react/renderer/core/LayoutConstraints.h index 5f838b5e4f2ba..26b4c419e38c6 100644 --- a/android/ReactCommon/react/renderer/core/LayoutConstraints.h +++ b/android/ReactCommon/react/renderer/core/LayoutConstraints.h @@ -21,8 +21,9 @@ namespace react { */ struct LayoutConstraints { Size minimumSize{0, 0}; - Size maximumSize{std::numeric_limits::infinity(), - std::numeric_limits::infinity()}; + Size maximumSize{ + std::numeric_limits::infinity(), + std::numeric_limits::infinity()}; LayoutDirection layoutDirection{LayoutDirection::Undefined}; /* @@ -39,6 +40,12 @@ inline bool operator==( std::tie(rhs.minimumSize, rhs.maximumSize, rhs.layoutDirection); } +inline bool operator!=( + const LayoutConstraints &lhs, + const LayoutConstraints &rhs) { + return !(lhs == rhs); +} + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/LayoutMetrics.cpp b/android/ReactCommon/react/renderer/core/LayoutMetrics.cpp new file mode 100644 index 0000000000000..26a3feaafeb5b --- /dev/null +++ b/android/ReactCommon/react/renderer/core/LayoutMetrics.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include "LayoutMetrics.h" + +namespace facebook { +namespace react { + +#if RN_DEBUG_STRING_CONVERTIBLE + +std::string getDebugName(LayoutMetrics const &object) { + return "LayoutMetrics"; +} + +std::vector getDebugProps( + LayoutMetrics const &object, + DebugStringConvertibleOptions options) { + return { + {"frame", + "{x:" + getDebugDescription(object.frame.origin.x, {}) + + ",y:" + getDebugDescription(object.frame.origin.y, {}) + + ",width:" + getDebugDescription(object.frame.size.width, {}) + + ",height:" + getDebugDescription(object.frame.size.height, {}) + + "}"}, + {"contentInsets", + "{top:" + getDebugDescription(object.contentInsets.top, {}) + + ",right:" + getDebugDescription(object.contentInsets.right, {}) + + ",bottom:" + getDebugDescription(object.contentInsets.bottom, {}) + + ",left:" + getDebugDescription(object.contentInsets.left, {}) + "}"}, + {"borderWidth", + "{top:" + getDebugDescription(object.borderWidth.top, {}) + + ",right:" + getDebugDescription(object.borderWidth.right, {}) + + ",bottom:" + getDebugDescription(object.borderWidth.bottom, {}) + + ",left:" + getDebugDescription(object.borderWidth.left, {}) + "}"}, + {"overflowInset", + "{top:" + getDebugDescription(object.overflowInset.top, {}) + + ",right:" + getDebugDescription(object.overflowInset.right, {}) + + ",bottom:" + getDebugDescription(object.overflowInset.bottom, {}) + + ",left:" + getDebugDescription(object.overflowInset.left, {}) + "}"}, + {"displayType", + object.displayType == DisplayType::None + ? "None" + : (object.displayType == DisplayType::Flex ? "Flex" : "Inline")}, + {"layoutDirection", + object.layoutDirection == LayoutDirection::Undefined + ? "Undefined" + : (object.layoutDirection == LayoutDirection::LeftToRight + ? "LeftToRight" + : "RightToLeft")}, + {"pointScaleFactor", + getDebugDescription(object.pointScaleFactor, options)}, + }; +} + +#endif + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/LayoutMetrics.h b/android/ReactCommon/react/renderer/core/LayoutMetrics.h index 603a56beab867..f094e2c55276e 100644 --- a/android/ReactCommon/react/renderer/core/LayoutMetrics.h +++ b/android/ReactCommon/react/renderer/core/LayoutMetrics.h @@ -7,7 +7,10 @@ #pragma once +#include #include +#include +#include #include namespace facebook { @@ -28,8 +31,9 @@ struct LayoutMetrics { Rect getContentFrame() const { return Rect{ Point{contentInsets.left, contentInsets.top}, - Size{frame.size.width - contentInsets.left - contentInsets.right, - frame.size.height - contentInsets.top - contentInsets.bottom}}; + Size{ + frame.size.width - contentInsets.left - contentInsets.right, + frame.size.height - contentInsets.top - contentInsets.bottom}}; } bool operator==(const LayoutMetrics &rhs) const { @@ -39,14 +43,16 @@ struct LayoutMetrics { this->borderWidth, this->displayType, this->layoutDirection, - this->pointScaleFactor) == + this->pointScaleFactor, + this->overflowInset) == std::tie( rhs.frame, rhs.contentInsets, rhs.borderWidth, rhs.displayType, rhs.layoutDirection, - rhs.pointScaleFactor); + rhs.pointScaleFactor, + rhs.overflowInset); } bool operator!=(const LayoutMetrics &rhs) const { @@ -62,5 +68,33 @@ struct LayoutMetrics { static LayoutMetrics const EmptyLayoutMetrics = { /* .frame = */ {{0, 0}, {-1.0, -1.0}}}; +#ifdef RN_DEBUG_STRING_CONVERTIBLE + +std::string getDebugName(LayoutMetrics const &object); +std::vector getDebugProps( + LayoutMetrics const &object, + DebugStringConvertibleOptions options); + +#endif + } // namespace react } // namespace facebook + +namespace std { + +template <> +struct hash { + size_t operator()(const facebook::react::LayoutMetrics &layoutMetrics) const { + return folly::hash::hash_combine( + 0, + layoutMetrics.frame, + layoutMetrics.contentInsets, + layoutMetrics.borderWidth, + layoutMetrics.displayType, + layoutMetrics.layoutDirection, + layoutMetrics.pointScaleFactor, + layoutMetrics.overflowInset); + } +}; + +} // namespace std diff --git a/android/ReactCommon/react/renderer/core/LayoutPrimitives.h b/android/ReactCommon/react/renderer/core/LayoutPrimitives.h index 14ae19126480c..b0981f6f73677 100644 --- a/android/ReactCommon/react/renderer/core/LayoutPrimitives.h +++ b/android/ReactCommon/react/renderer/core/LayoutPrimitives.h @@ -18,18 +18,18 @@ namespace react { * engine which should be used for laying out the node. */ enum class DisplayType { - None, - Flex, - Inline, + None = 0, + Flex = 1, + Inline = 2, }; /* * User interface layout direction. */ enum class LayoutDirection { - Undefined, - LeftToRight, - RightToLeft, + Undefined = 0, + LeftToRight = 1, + RightToLeft = 2, }; } // namespace react @@ -42,4 +42,12 @@ struct hash { return hash()(static_cast(v)); } }; + +template <> +struct hash { + size_t operator()(const facebook::react::DisplayType &v) const { + return hash()(static_cast(v)); + } +}; + } // namespace std diff --git a/android/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/android/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index 3809740c83e72..a1ad4dda15f72 100644 --- a/android/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/android/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -159,13 +159,6 @@ Point LayoutableShadowNode::getContentOriginOffset() const { return {0, 0}; } -LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics( - LayoutableShadowNode const &ancestorLayoutableShadowNode, - LayoutInspectingPolicy policy) const { - return computeRelativeLayoutMetrics( - getFamily(), ancestorLayoutableShadowNode, policy); -} - LayoutableShadowNode::UnsharedList LayoutableShadowNode::getLayoutableChildNodes() const { LayoutableShadowNode::UnsharedList layoutableChildren; @@ -223,7 +216,7 @@ ShadowNode::Shared LayoutableShadowNode::findNodeAtPoint( ShadowNode::Shared node, Point point) { auto layoutableShadowNode = - dynamic_cast(node.get()); + traitCast(node.get()); if (!layoutableShadowNode) { return nullptr; diff --git a/android/ReactCommon/react/renderer/core/LayoutableShadowNode.h b/android/ReactCommon/react/renderer/core/LayoutableShadowNode.h index 83ad3273a1397..b816b54a2cfca 100644 --- a/android/ReactCommon/react/renderer/core/LayoutableShadowNode.h +++ b/android/ReactCommon/react/renderer/core/LayoutableShadowNode.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -122,21 +123,6 @@ class LayoutableShadowNode : public ShadowNode { */ virtual Point getContentOriginOffset() const; - /* - * Returns layout metrics relatively to the given ancestor node. - * Uses `computeRelativeLayoutMetrics()` under the hood. - */ - LayoutMetrics getRelativeLayoutMetrics( - ShadowNodeFamily const &descendantNodeFamily, - LayoutInspectingPolicy policy) const; - - /* - * Returns layout metrics relatively to the given ancestor node. - */ - LayoutMetrics getRelativeLayoutMetrics( - LayoutableShadowNode const &ancestorLayoutableShadowNode, - LayoutInspectingPolicy policy) const; - /* * Sets layout metrics for the shadow node. */ @@ -184,10 +170,7 @@ inline LayoutableShadowNode const &traitCast( ShadowNode const &shadowNode) { bool castable = shadowNode.getTraits().check(ShadowNodeTraits::Trait::LayoutableKind); - assert( - castable == - (dynamic_cast(&shadowNode) != nullptr)); - assert(castable); + react_native_assert(castable); (void)castable; return static_cast(shadowNode); } @@ -200,9 +183,6 @@ inline LayoutableShadowNode const *traitCast( } bool castable = shadowNode->getTraits().check(ShadowNodeTraits::Trait::LayoutableKind); - assert( - castable == - (dynamic_cast(shadowNode) != nullptr)); if (!castable) { return nullptr; } diff --git a/android/ReactCommon/react/renderer/core/Props.cpp b/android/ReactCommon/react/renderer/core/Props.cpp index 79d6777691f36..f502fccfdf241 100644 --- a/android/ReactCommon/react/renderer/core/Props.cpp +++ b/android/ReactCommon/react/renderer/core/Props.cpp @@ -13,8 +13,16 @@ namespace facebook { namespace react { -Props::Props(const Props &sourceProps, const RawProps &rawProps) - : nativeId(convertRawProp(rawProps, "nativeID", sourceProps.nativeId, {})), +Props::Props( + const PropsParserContext &context, + const Props &sourceProps, + const RawProps &rawProps) + : nativeId(convertRawProp( + context, + rawProps, + "nativeID", + sourceProps.nativeId, + {})), revision(sourceProps.revision + 1) #ifdef ANDROID , diff --git a/android/ReactCommon/react/renderer/core/Props.h b/android/ReactCommon/react/renderer/core/Props.h index 5653509fd7adf..5d99a0139a1e8 100644 --- a/android/ReactCommon/react/renderer/core/Props.h +++ b/android/ReactCommon/react/renderer/core/Props.h @@ -9,6 +9,8 @@ #include +#include +#include #include #include #include @@ -28,7 +30,10 @@ class Props : public virtual Sealable, public virtual DebugStringConvertible { using Shared = std::shared_ptr; Props() = default; - Props(Props const &sourceProps, RawProps const &rawProps); + Props( + const PropsParserContext &context, + const Props &sourceProps, + RawProps const &rawProps); virtual ~Props() = default; std::string nativeId; diff --git a/android/ReactCommon/react/renderer/core/PropsParserContext.h b/android/ReactCommon/react/renderer/core/PropsParserContext.h new file mode 100644 index 0000000000000..df4b16d70a596 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/PropsParserContext.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +// For props requiring some context to parse, this toolbox can be used. +// It should be used as infrequently as possible - most props can and should +// be parsed without any context. +struct PropsParserContext { + // Non-copyable + PropsParserContext(const PropsParserContext &) = delete; + PropsParserContext &operator=(const PropsParserContext &) = delete; + + SurfaceId const surfaceId; + ContextContainer const &contextContainer; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/RawEvent.cpp b/android/ReactCommon/react/renderer/core/RawEvent.cpp index 7782e7cbe8bdf..3abace5171e2e 100644 --- a/android/ReactCommon/react/renderer/core/RawEvent.cpp +++ b/android/ReactCommon/react/renderer/core/RawEvent.cpp @@ -13,10 +13,12 @@ namespace react { RawEvent::RawEvent( std::string type, ValueFactory payloadFactory, - SharedEventTarget eventTarget) + SharedEventTarget eventTarget, + Category category) : type(std::move(type)), payloadFactory(std::move(payloadFactory)), - eventTarget(std::move(eventTarget)) {} + eventTarget(std::move(eventTarget)), + category(category) {} } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/RawEvent.h b/android/ReactCommon/react/renderer/core/RawEvent.h index 03286a2ac089c..ff50464d4a3e6 100644 --- a/android/ReactCommon/react/renderer/core/RawEvent.h +++ b/android/ReactCommon/react/renderer/core/RawEvent.h @@ -19,16 +19,55 @@ namespace react { /* * Represents ready-to-dispatch event object. */ -class RawEvent { - public: +struct RawEvent { + /* + * Defines category of a native platform event. This is used to deduce types + * of events for Concurrent Mode. + * This enum is duplicated for JNI access in `EventCategoryDef.java`, keep in + * sync. + */ + enum class Category { + /* + * Start of a continuous event. To be used with touchStart. + */ + ContinuousStart = 0, + + /* + * End of a continuous event. To be used with touchEnd. + */ + ContinuousEnd = 1, + + /* + * Priority for this event will be determined from other events in the + * queue. If it is triggered by continuous event, its priority will be + * default. If it is not triggered by continuous event, its priority will be + * discrete. + */ + Unspecified = 2, + + /* + * Forces discrete type for the event. Regardless if continuous event is + * ongoing. + */ + Discrete = 3, + + /* + * Forces continuous type for the event. Regardless if continuous event + * isn't ongoing. + */ + Continuous = 4 + }; + RawEvent( std::string type, ValueFactory payloadFactory, - SharedEventTarget eventTarget); + SharedEventTarget eventTarget, + Category category = Category::Unspecified); std::string type; ValueFactory payloadFactory; SharedEventTarget eventTarget; + Category category; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/core/RawProps.cpp b/android/ReactCommon/react/renderer/core/RawProps.cpp index fd55f40c0c5a1..4776ce5ac8108 100644 --- a/android/ReactCommon/react/renderer/core/RawProps.cpp +++ b/android/ReactCommon/react/renderer/core/RawProps.cpp @@ -7,6 +7,7 @@ #include "RawProps.h" +#include #include namespace facebook { @@ -46,8 +47,9 @@ RawProps::RawProps(folly::dynamic const &dynamic) noexcept { dynamic_ = dynamic; } -void RawProps::parse(RawPropsParser const &parser) const noexcept { - assert(parser_ == nullptr && "A parser was already assigned."); +void RawProps::parse(RawPropsParser const &parser, const PropsParserContext &) + const noexcept { + react_native_assert(parser_ == nullptr && "A parser was already assigned."); parser_ = &parser; parser.preparse(*this); } @@ -84,7 +86,7 @@ const RawValue *RawProps::at( char const *name, char const *prefix, char const *suffix) const noexcept { - assert( + react_native_assert( parser_ && "The object is not parsed. `parse` must be called before `at`."); return parser_->at(*this, RawPropsKey{prefix, name, suffix}); diff --git a/android/ReactCommon/react/renderer/core/RawProps.h b/android/ReactCommon/react/renderer/core/RawProps.h index 152f7528f9d21..3cb96eca5fedc 100644 --- a/android/ReactCommon/react/renderer/core/RawProps.h +++ b/android/ReactCommon/react/renderer/core/RawProps.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +75,8 @@ class RawProps final { RawProps(RawProps const &other) noexcept = delete; RawProps &operator=(RawProps const &other) noexcept = delete; - void parse(RawPropsParser const &parser) const noexcept; + void parse(RawPropsParser const &parser, const PropsParserContext &) + const noexcept; /* * Deprecated. Do not use. diff --git a/android/ReactCommon/react/renderer/core/RawPropsKey.cpp b/android/ReactCommon/react/renderer/core/RawPropsKey.cpp index 505eca510eb64..35c8283f5e24a 100644 --- a/android/ReactCommon/react/renderer/core/RawPropsKey.cpp +++ b/android/ReactCommon/react/renderer/core/RawPropsKey.cpp @@ -10,48 +10,59 @@ #include #include +#include #include namespace facebook { namespace react { -void RawPropsKey::render(char *buffer, RawPropsPropNameLength *length) const - noexcept { +void RawPropsKey::render(char *buffer, RawPropsPropNameLength *length) + const noexcept { *length = 0; // Prefix if (prefix) { - auto prefixLength = std::strlen(prefix); + auto prefixLength = + static_cast(std::strlen(prefix)); std::memcpy(buffer, prefix, prefixLength); *length = prefixLength; } // Name - auto nameLength = std::strlen(name); + auto nameLength = static_cast(std::strlen(name)); std::memcpy(buffer + *length, name, nameLength); *length += nameLength; // Suffix if (suffix) { - int suffixLength = std::strlen(suffix); + auto suffixLength = + static_cast(std::strlen(suffix)); std::memcpy(buffer + *length, suffix, suffixLength); *length += suffixLength; } - assert(*length < kPropNameLengthHardCap); + react_native_assert(*length < kPropNameLengthHardCap); } RawPropsKey::operator std::string() const noexcept { char buffer[kPropNameLengthHardCap]; RawPropsPropNameLength length = 0; render(buffer, &length); - assert(length < kPropNameLengthHardCap); + react_native_assert(length < kPropNameLengthHardCap); return std::string{buffer, length}; } +static bool areFieldsEqual(char const *lhs, char const *rhs) { + if (lhs == nullptr || rhs == nullptr) { + return lhs == rhs; + } + return std::string(lhs) == std::string(rhs); +} + bool operator==(RawPropsKey const &lhs, RawPropsKey const &rhs) noexcept { // Note: We check the name first. - return lhs.name == rhs.name && lhs.prefix == rhs.prefix && - lhs.suffix == rhs.suffix; + return areFieldsEqual(lhs.name, rhs.name) && + areFieldsEqual(lhs.prefix, rhs.prefix) && + areFieldsEqual(lhs.suffix, rhs.suffix); } bool operator!=(RawPropsKey const &lhs, RawPropsKey const &rhs) noexcept { diff --git a/android/ReactCommon/react/renderer/core/RawPropsKeyMap.cpp b/android/ReactCommon/react/renderer/core/RawPropsKeyMap.cpp index 889cdbead3603..af24fdb4a986b 100644 --- a/android/ReactCommon/react/renderer/core/RawPropsKeyMap.cpp +++ b/android/ReactCommon/react/renderer/core/RawPropsKeyMap.cpp @@ -7,6 +7,8 @@ #include "RawPropsKeyMap.h" +#include + #include #include #include @@ -61,30 +63,30 @@ void RawPropsKeyMap::reindex() noexcept { buckets_.resize(kPropNameLengthHardCap); auto length = RawPropsPropNameLength{0}; - for (auto i = 0; i < items_.size(); i++) { + for (size_t i = 0; i < items_.size(); i++) { auto &item = items_[i]; if (item.length != length) { for (auto j = length; j < item.length; j++) { - buckets_[j] = i; + buckets_[j] = static_cast(i); } length = item.length; } } for (auto j = length; j < buckets_.size(); j++) { - buckets_[j] = items_.size(); + buckets_[j] = static_cast(items_.size()); } } RawPropsValueIndex RawPropsKeyMap::at( char const *name, RawPropsPropNameLength length) noexcept { - assert(length > 0); - assert(length < kPropNameLengthHardCap); + react_native_assert(length > 0); + react_native_assert(length < kPropNameLengthHardCap); // 1. Find the bucket. auto lower = int{buckets_[length - 1]}; auto upper = int{buckets_[length]} - 1; - assert(lower - 1 <= upper); + react_native_assert(lower - 1 <= upper); // 2. Binary search in the bucket. while (lower <= upper) { diff --git a/android/ReactCommon/react/renderer/core/RawPropsParser.cpp b/android/ReactCommon/react/renderer/core/RawPropsParser.cpp index 5735302873a64..db098e9fab5bc 100644 --- a/android/ReactCommon/react/renderer/core/RawPropsParser.cpp +++ b/android/ReactCommon/react/renderer/core/RawPropsParser.cpp @@ -8,6 +8,7 @@ #include "RawPropsParser.h" #include +#include #include #include @@ -40,7 +41,7 @@ RawValue const *RawPropsParser::at( // This is not thread-safe part; this happens only during initialization of // a `ComponentDescriptor` where it is actually safe. keys_.push_back(key); - nameToIndex_.insert(key, size_); + nameToIndex_.insert(key, static_cast(size_)); size_++; return nullptr; } @@ -62,14 +63,14 @@ RawValue const *RawPropsParser::at( // the same order every time. This is trivial if you have a simple Props // constructor, but difficult or impossible if you have a shared sub-prop // Struct that is used by multiple parent Props. -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG bool resetLoop = false; #endif do { rawProps.keyIndexCursor_++; if (UNLIKELY(rawProps.keyIndexCursor_ >= size_)) { -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG if (resetLoop) { LOG(ERROR) << "Looked up RawProps key that does not exist: " << (std::string)key; @@ -106,20 +107,21 @@ void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { if (!rawProps.value_.isObject()) { LOG(ERROR) << "Preparse props: rawProps value is not object"; } - assert(rawProps.value_.isObject()); + react_native_assert(rawProps.value_.isObject()); auto object = rawProps.value_.asObject(runtime); auto names = object.getPropertyNames(runtime); auto count = names.size(runtime); auto valueIndex = RawPropsValueIndex{0}; - for (auto i = 0; i < count; i++) { + for (size_t i = 0; i < count; i++) { auto nameValue = names.getValueAtIndex(runtime, i).getString(runtime); auto value = object.getProperty(runtime, nameValue); auto name = nameValue.utf8(runtime); - auto keyIndex = nameToIndex_.at(name.data(), name.size()); + auto keyIndex = nameToIndex_.at( + name.data(), static_cast(name.size())); if (keyIndex == kRawPropsValueIndexEmpty) { continue; } @@ -140,7 +142,8 @@ void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { for (auto const &pair : dynamic.items()) { auto name = pair.first.getString(); - auto keyIndex = nameToIndex_.at(name.data(), name.size()); + auto keyIndex = nameToIndex_.at( + name.data(), static_cast(name.size())); if (keyIndex == kRawPropsValueIndexEmpty) { continue; } diff --git a/android/ReactCommon/react/renderer/core/RawPropsParser.h b/android/ReactCommon/react/renderer/core/RawPropsParser.h index 4dc557b708f9f..5d37cefd8c3ba 100644 --- a/android/ReactCommon/react/renderer/core/RawPropsParser.h +++ b/android/ReactCommon/react/renderer/core/RawPropsParser.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -40,8 +41,16 @@ class RawPropsParser final { std::is_base_of::value, "PropsT must be a descendant of Props"); RawProps emptyRawProps{}; - emptyRawProps.parse(*this); - PropsT({}, emptyRawProps); + + // Create a stub parser context. + // Since this prepares the parser by passing in + // empty props, no prop parsers should actually reference the + // ContextContainer or SurfaceId here. + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + emptyRawProps.parse(*this, parserContext); + PropsT(parserContext, {}, emptyRawProps); postPrepare(); } @@ -64,8 +73,8 @@ class RawPropsParser final { /* * To be used by `RawProps` only. */ - RawValue const *at(RawProps const &rawProps, RawPropsKey const &key) const - noexcept; + RawValue const *at(RawProps const &rawProps, RawPropsKey const &key) + const noexcept; mutable better::small_vector keys_{}; diff --git a/android/ReactCommon/react/renderer/core/RawValue.h b/android/ReactCommon/react/renderer/core/RawValue.h index 75c2e9527a7cb..16cab0bd7b9c4 100644 --- a/android/ReactCommon/react/renderer/core/RawValue.h +++ b/android/ReactCommon/react/renderer/core/RawValue.h @@ -12,6 +12,8 @@ #include #include +#include + namespace facebook { namespace react { @@ -189,7 +191,7 @@ class RawValue { } for (const auto &item : dynamic.items()) { - assert(item.first.isString()); + react_native_assert(item.first.isString()); if (!checkValueType(item.second, (T *)nullptr)) { return false; } @@ -213,7 +215,7 @@ class RawValue { } static int castValue(const folly::dynamic &dynamic, int *type) noexcept { - return dynamic.asInt(); + return static_cast(dynamic.asInt()); } static int64_t castValue( @@ -223,7 +225,7 @@ class RawValue { } static float castValue(const folly::dynamic &dynamic, float *type) noexcept { - return dynamic.asDouble(); + return static_cast(dynamic.asDouble()); } static double castValue( @@ -242,7 +244,7 @@ class RawValue { static std::vector castValue( const folly::dynamic &dynamic, std::vector *type) noexcept { - assert(dynamic.isArray()); + react_native_assert(dynamic.isArray()); auto result = std::vector{}; result.reserve(dynamic.size()); for (const auto &item : dynamic) { @@ -255,7 +257,7 @@ class RawValue { static std::vector> castValue( const folly::dynamic &dynamic, std::vector> *type) noexcept { - assert(dynamic.isArray()); + react_native_assert(dynamic.isArray()); auto result = std::vector>{}; result.reserve(dynamic.size()); for (const auto &item : dynamic) { @@ -268,10 +270,10 @@ class RawValue { static better::map castValue( const folly::dynamic &dynamic, better::map *type) noexcept { - assert(dynamic.isObject()); + react_native_assert(dynamic.isObject()); auto result = better::map{}; for (const auto &item : dynamic.items()) { - assert(item.first.isString()); + react_native_assert(item.first.isString()); result[item.first.getString()] = castValue(item.second, (T *)nullptr); } return result; diff --git a/android/ReactCommon/react/renderer/core/ReactEventPriority.h b/android/ReactCommon/react/renderer/core/ReactEventPriority.h new file mode 100644 index 0000000000000..3ef8b5728ebd4 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/ReactEventPriority.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook { +namespace react { + +/* + * An enum that represents React's event priority. + * Used to map Fabric events to React. + */ +enum class ReactEventPriority { + /* + * Event priority is unspecified. + */ + Default, + + /* + * User events that happen at discrete times, like + * input into text field. + */ + Discrete, + + /* + * “fluid” user events that happen many times over a short period of time like + * scrolling. + */ + Continuous, +}; + +static constexpr std::underlying_type::type serialize( + ReactEventPriority reactEventPriority) { + return static_cast::type>( + reactEventPriority); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/ReactPrimitives.h b/android/ReactCommon/react/renderer/core/ReactPrimitives.h index d72fa25d59301..28738394bf9e9 100644 --- a/android/ReactCommon/react/renderer/core/ReactPrimitives.h +++ b/android/ReactCommon/react/renderer/core/ReactPrimitives.h @@ -8,8 +8,6 @@ #pragma once #include -#include -#include #include #include @@ -42,5 +40,39 @@ using ComponentHandle = int64_t; */ using ComponentName = char const *; +/* + * Defines how visual side effects (views, images, text, and so on) are + * mounted (on not) on the screen. + */ +enum class DisplayMode { + /* + * The surface is running normally. All visual side-effects will be rendered + * on the screen. + */ + Visible = 0, + + /* + * The surface is `Suspended`. All new (committed after switching to the + * mode) visual side-effects will *not* be mounted on the screen (the screen + * will stop updating). + * + * The mode can be used for preparing a surface for possible future use. + * The surface will be prepared without spending computing resources + * on mounting, and then can be instantly mounted if needed. + */ + Suspended = 1, + + /* + * The surface is `Hidden`. All previously mounted visual side-effects + * will be unmounted, and all new (committed after switching to the mode) + * visual side-effects will *not* be mounted on the screen until the mode is + * switched back to `normal`. + * + * The mode can be used for temporarily freeing computing resources of + * off-the-screen surfaces. + */ + Hidden = 2, +}; + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/Sealable.cpp b/android/ReactCommon/react/renderer/core/Sealable.cpp index 5bc35e6908b68..2326a4ed7b2f7 100644 --- a/android/ReactCommon/react/renderer/core/Sealable.cpp +++ b/android/ReactCommon/react/renderer/core/Sealable.cpp @@ -7,7 +7,8 @@ #include "Sealable.h" -#include +#include +#include namespace facebook { namespace react { @@ -24,7 +25,7 @@ namespace react { * http://en.cppreference.com/w/cpp/language/rule_of_three */ -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG Sealable::Sealable() : sealed_(false) {} @@ -56,7 +57,7 @@ bool Sealable::getSealed() const { } void Sealable::ensureUnsealed() const { - assert(!sealed_ && "Attempt to mutate a sealed object."); + react_native_assert(!sealed_ && "Attempt to mutate a sealed object."); } #endif diff --git a/android/ReactCommon/react/renderer/core/Sealable.h b/android/ReactCommon/react/renderer/core/Sealable.h index 2ca6f64aa6502..f57c42eda5ac2 100644 --- a/android/ReactCommon/react/renderer/core/Sealable.h +++ b/android/ReactCommon/react/renderer/core/Sealable.h @@ -9,6 +9,8 @@ #include +#include + namespace facebook { namespace react { @@ -42,8 +44,9 @@ namespace react { * must be prevented. */ -#ifdef NDEBUG +#ifndef REACT_NATIVE_DEBUG +// Release-mode, production version class Sealable { public: inline void seal() const {} @@ -55,6 +58,7 @@ class Sealable { #else +// Debug version class Sealable { public: Sealable(); diff --git a/android/ReactCommon/react/renderer/core/ShadowNode.cpp b/android/ReactCommon/react/renderer/core/ShadowNode.cpp index a5839fc820d80..4c7258c4cbb77 100644 --- a/android/ReactCommon/react/renderer/core/ShadowNode.cpp +++ b/android/ReactCommon/react/renderer/core/ShadowNode.cpp @@ -6,10 +6,12 @@ */ #include "ShadowNode.h" +#include "DynamicPropsUtilities.h" #include "ShadowNodeFragment.h" #include +#include #include #include #include @@ -24,6 +26,30 @@ SharedShadowNodeSharedList ShadowNode::emptySharedShadowNodeSharedList() { return emptySharedShadowNodeSharedList; } +/* + * On iOS, this method returns `props` if provided, `sourceShadowNode`'s props + * otherwise. On Android, we forward props in case `sourceShadowNode` hasn't + * been mounted. `Props::rawProps` are merged from `props` to a copy of + * `sourceShadowNode.props_` and returned. This is necessary to enable + * Background Executor and should be removed once reimplementation of JNI layer + * is finished. + */ +SharedProps ShadowNode::propsForClonedShadowNode( + ShadowNode const &sourceShadowNode, + Props::Shared const &props) { +#ifdef ANDROID + bool hasBeenMounted = sourceShadowNode.hasBeenMounted_; + bool sourceNodeHasRawProps = !sourceShadowNode.getProps()->rawProps.empty(); + if (!hasBeenMounted && sourceNodeHasRawProps && props) { + auto &castedProps = const_cast(*props); + castedProps.rawProps = mergeDynamicProps( + sourceShadowNode.getProps()->rawProps, props->rawProps); + return props; + } +#endif + return props ? props : sourceShadowNode.getProps(); +} + bool ShadowNode::sameFamily(const ShadowNode &first, const ShadowNode &second) { return first.family_ == second.family_; } @@ -46,8 +72,8 @@ ShadowNode::ShadowNode( orderIndex_(0), family_(family), traits_(traits) { - assert(props_); - assert(children_); + react_native_assert(props_); + react_native_assert(children_); traits_.set(ShadowNodeTraits::Trait::ChildrenAreShared); @@ -60,13 +86,13 @@ ShadowNode::ShadowNode( } ShadowNode::ShadowNode( - const ShadowNode &sourceShadowNode, - const ShadowNodeFragment &fragment) + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment) : #if RN_DEBUG_STRING_CONVERTIBLE revision_(sourceShadowNode.revision_ + 1), #endif - props_(fragment.props ? fragment.props : sourceShadowNode.props_), + props_(propsForClonedShadowNode(sourceShadowNode, fragment.props)), children_( fragment.children ? fragment.children : sourceShadowNode.children_), state_( @@ -76,8 +102,8 @@ ShadowNode::ShadowNode( family_(sourceShadowNode.family_), traits_(sourceShadowNode.traits_) { - assert(props_); - assert(children_); + react_native_assert(props_); + react_native_assert(children_); traits_.set(ShadowNodeTraits::Trait::ChildrenAreShared); @@ -88,7 +114,8 @@ ShadowNode::ShadowNode( } } -UnsharedShadowNode ShadowNode::clone(const ShadowNodeFragment &fragment) const { +ShadowNode::Unshared ShadowNode::clone( + const ShadowNodeFragment &fragment) const { return family_->componentDescriptor_.cloneShadowNode(*this, fragment); } @@ -183,7 +210,7 @@ void ShadowNode::replaceChild( *std::const_pointer_cast(children_); auto size = children.size(); - if (suggestedIndex != -1 && suggestedIndex < size) { + if (suggestedIndex != -1 && static_cast(suggestedIndex) < size) { // If provided `suggestedIndex` is accurate, // replacing in place using the index. if (children.at(suggestedIndex).get() == &oldChild) { @@ -192,14 +219,14 @@ void ShadowNode::replaceChild( } } - for (auto index = 0; index < size; index++) { + for (size_t index = 0; index < size; index++) { if (children.at(index).get() == &oldChild) { children[index] = newChild; return; } } - assert(false && "Child to replace was not found."); + react_native_assert(false && "Child to replace was not found."); } void ShadowNode::cloneChildrenIfShared() { @@ -214,6 +241,7 @@ void ShadowNode::cloneChildrenIfShared() { void ShadowNode::setMounted(bool mounted) const { if (mounted) { family_->setMostRecentState(getState()); + hasBeenMounted_ = mounted; } family_->eventEmitter_->setEnabled(mounted); @@ -238,7 +266,7 @@ ShadowNode::Unshared ShadowNode::cloneTree( auto newShadowNode = callback(*oldShadowNode); - assert( + react_native_assert( newShadowNode && "`callback` returned `nullptr` which is not allowed value."); @@ -249,7 +277,8 @@ ShadowNode::Unshared ShadowNode::cloneTree( auto childIndex = it->second; auto children = parentNode.getChildren(); - assert(ShadowNode::sameFamily(*children.at(childIndex), *childNode)); + react_native_assert( + ShadowNode::sameFamily(*children.at(childIndex), *childNode)); children[childIndex] = childNode; childNode = parentNode.clone({ diff --git a/android/ReactCommon/react/renderer/core/ShadowNode.h b/android/ReactCommon/react/renderer/core/ShadowNode.h index 30f5774b7d5f2..1ef71355f06f1 100644 --- a/android/ReactCommon/react/renderer/core/ShadowNode.h +++ b/android/ReactCommon/react/renderer/core/ShadowNode.h @@ -33,7 +33,6 @@ class ShadowNode; // Deprecated: Use ShadowNode::Shared instead using SharedShadowNode = std::shared_ptr; using WeakShadowNode = std::weak_ptr; -using UnsharedShadowNode = std::shared_ptr; using SharedShadowNodeList = better::small_vector; using SharedShadowNodeSharedList = std::shared_ptr; @@ -46,8 +45,11 @@ class ShadowNode : public Sealable, public DebugStringConvertible { using Unshared = std::shared_ptr; using ListOfShared = better::small_vector; + using ListOfWeak = + better::small_vector; using SharedListOfShared = std::shared_ptr; using UnsharedListOfShared = std::shared_ptr; + using UnsharedListOfWeak = std::shared_ptr; using AncestorList = better::small_vector< std::pair< @@ -101,7 +103,7 @@ class ShadowNode : public Sealable, public DebugStringConvertible { /* * Clones the shadow node using stored `cloneFunction`. */ - UnsharedShadowNode clone(const ShadowNodeFragment &fragment) const; + ShadowNode::Unshared clone(const ShadowNodeFragment &fragment) const; /* * Clones the node (and partially the tree starting from the node) by @@ -176,8 +178,6 @@ class ShadowNode : public Sealable, public DebugStringConvertible { */ void setMounted(bool mounted) const; - int getStateRevision() const; - #pragma mark - DebugStringConvertible #if RN_DEBUG_STRING_CONVERTIBLE @@ -214,6 +214,12 @@ class ShadowNode : public Sealable, public DebugStringConvertible { */ ShadowNodeFamily::Shared family_; + mutable std::atomic hasBeenMounted_{false}; + + static SharedProps propsForClonedShadowNode( + ShadowNode const &sourceShadowNode, + Props::Shared const &props); + protected: /* * Traits associated with the particular `ShadowNode` class and an instance of @@ -234,5 +240,9 @@ ShadowNodeReferenceT traitCast(ShadowNode const &shadowNode); template ShadowNodePointerT traitCast(ShadowNode const *shadowNode); +template +std::shared_ptr traitCast( + std::shared_ptr shadowNode); + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp b/android/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp index c76b3ab8d3aec..f4c0644255601 100644 --- a/android/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp +++ b/android/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp @@ -8,6 +8,7 @@ #include "ShadowNodeFamily.h" #include "ShadowNode.h" +#include #include #include @@ -29,7 +30,7 @@ ShadowNodeFamily::ShadowNodeFamily( componentName_(componentDescriptor.getComponentName()) {} void ShadowNodeFamily::setParent(ShadowNodeFamily::Shared const &parent) const { - assert(parent_.lock() == nullptr || parent_.lock() == parent); + react_native_assert(parent_.lock() == nullptr || parent_.lock() == parent); if (hasParent_) { return; } diff --git a/android/ReactCommon/react/renderer/core/ShadowNodeFamily.h b/android/ReactCommon/react/renderer/core/ShadowNodeFamily.h index ad3ecf0e36d09..07746e35501be 100644 --- a/android/ReactCommon/react/renderer/core/ShadowNodeFamily.h +++ b/android/ReactCommon/react/renderer/core/ShadowNodeFamily.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -26,7 +27,7 @@ class State; * Represents all things that shadow nodes from the same family have in common. * To be used inside `ShadowNode` class *only*. */ -class ShadowNodeFamily { +class ShadowNodeFamily final { public: using Shared = std::shared_ptr; using Weak = std::weak_ptr; diff --git a/android/ReactCommon/react/renderer/core/ShadowNodeTraits.h b/android/ReactCommon/react/renderer/core/ShadowNodeTraits.h index 16b7129bd00c7..7c02f4bea94a6 100644 --- a/android/ReactCommon/react/renderer/core/ShadowNodeTraits.h +++ b/android/ReactCommon/react/renderer/core/ShadowNodeTraits.h @@ -21,7 +21,7 @@ class ShadowNodeTraits { public: /* * Underlying type for the traits. - * The first 16 bits are reserved for Core. + * The first 23 bits are reserved for Core. */ enum Trait : int32_t { None = 0, @@ -54,16 +54,25 @@ class ShadowNodeTraits { // Nodes with this trait (and all their descendants) will not produce views. Hidden = 1 << 6, + // Indicates that the `YogaLayoutableShadowNode` must set `isDirty` flag for + // Yoga node when a `ShadowNode` is being cloned. `ShadowNode`s that modify + // Yoga styles in the constructor (or later) *after* the `ShadowNode` + // is cloned must set this trait. + // Any Yoga node (not only Leaf ones) can have this trait. + DirtyYogaNode = 1 << 9, + // Inherits `YogaLayoutableShadowNode` and enforces that the `YGNode` is a // leaf. LeafYogaNode = 1 << 10, - // Inherits `LayoutableShadowNode` and calls `measure()`. - HasMeasure = 1 << 11, + // Inherits `YogaLayoutableShadowNode` and has a custom measure function. + // Only Leaf nodes can have this trait. + MeasurableYogaNode = 1 << 11, - // Indicates that the `ShadowNode` must form a stacking context (a level - // of the hierarchy; `ShadowView`s formed by descendants the node will be - // descendants of a `ShadowView` formed by the node). + // Indicates that the `ShadowNode` must form a stacking context. + // A Stacking Context forms a level of a `ShadowView` hierarchy (in contrast + // with a level of a `ShadowNode` hierarchy). + // See W3C standard for more details: https://www.w3.org/TR/CSS2/zindex.html FormsStackingContext = 1 << 13, // Indicates that the node must form a `ShadowView`. @@ -73,6 +82,22 @@ class ShadowNodeTraits { // Indicates that `children` list is shared between nodes and need // to be cloned before the first mutation. ChildrenAreShared = 1 << 15, + + // Inherits 'RawTextShadowNode' + RawText = 1 << 16, + + // Inherits 'TextShadowNode' + Text = 1 << 17, + + // Reserved + ReservedTrait1 = 1 << 18, + ReservedTrait2 = 1 << 19, + ReservedTrait3 = 1 << 20, + ReservedTrait4 = 1 << 21, + ReservedTrait5 = 1 << 22, + + // Unserved - alias these for local usage + UnreservedTrait1 = 1 << 23 }; /* diff --git a/android/ReactCommon/react/renderer/core/State.h b/android/ReactCommon/react/renderer/core/State.h index ae636f37e421c..ce5b51fe065e0 100644 --- a/android/ReactCommon/react/renderer/core/State.h +++ b/android/ReactCommon/react/renderer/core/State.h @@ -9,6 +9,8 @@ #ifdef ANDROID #include +#include +#include #endif #include @@ -65,9 +67,8 @@ class State { #ifdef ANDROID virtual folly::dynamic getDynamic() const = 0; - virtual void updateState( - folly::dynamic data, - std::function failureCallback) const = 0; + virtual MapBuffer getMapBuffer() const = 0; + virtual void updateState(folly::dynamic data) const = 0; #endif protected: diff --git a/android/ReactCommon/react/renderer/core/StateData.h b/android/ReactCommon/react/renderer/core/StateData.h index b13b5950490b8..36e098d1cd647 100644 --- a/android/ReactCommon/react/renderer/core/StateData.h +++ b/android/ReactCommon/react/renderer/core/StateData.h @@ -11,6 +11,8 @@ #ifdef ANDROID #include +#include +#include #endif namespace facebook { @@ -27,6 +29,7 @@ struct StateData final { StateData() = default; StateData(StateData const &previousState, folly::dynamic data){}; folly::dynamic getDynamic() const; + MapBuffer getMapBuffer() const; #endif }; diff --git a/android/ReactCommon/react/renderer/core/StateUpdate.h b/android/ReactCommon/react/renderer/core/StateUpdate.h index 7931f5e080066..890e56d141092 100644 --- a/android/ReactCommon/react/renderer/core/StateUpdate.h +++ b/android/ReactCommon/react/renderer/core/StateUpdate.h @@ -25,8 +25,6 @@ class StateUpdate { SharedShadowNodeFamily family; Callback callback; - FailureCallback failureCallback; - bool autorepeat; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/core/UnbatchedEventQueue.cpp b/android/ReactCommon/react/renderer/core/UnbatchedEventQueue.cpp index 4de0e8c747d85..98210411f7482 100644 --- a/android/ReactCommon/react/renderer/core/UnbatchedEventQueue.cpp +++ b/android/ReactCommon/react/renderer/core/UnbatchedEventQueue.cpp @@ -11,8 +11,6 @@ namespace facebook { namespace react { void UnbatchedEventQueue::onEnqueue() const { - EventQueue::onEnqueue(); - eventBeat_->request(); eventBeat_->induce(); } diff --git a/android/ReactCommon/react/renderer/core/conversions.h b/android/ReactCommon/react/renderer/core/conversions.h index 9dc3daa994fa8..70dda9a9f5589 100644 --- a/android/ReactCommon/react/renderer/core/conversions.h +++ b/android/ReactCommon/react/renderer/core/conversions.h @@ -34,6 +34,17 @@ inline int toInt(const LayoutDirection &layoutDirection) { } } +inline int toInt(const DisplayType &displayType) { + switch (displayType) { + case DisplayType::None: + return 0; + case DisplayType::Flex: + return 1; + case DisplayType::Inline: + return 2; + } +} + inline std::string toString(const DisplayType &displayType) { switch (displayType) { case DisplayType::None: diff --git a/android/ReactCommon/react/renderer/core/propsConversions.h b/android/ReactCommon/react/renderer/core/propsConversions.h index cb3f1665fc0fb..55e78f24303ac 100644 --- a/android/ReactCommon/react/renderer/core/propsConversions.h +++ b/android/ReactCommon/react/renderer/core/propsConversions.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,20 +20,26 @@ namespace facebook { namespace react { template -void fromRawValue(RawValue const &rawValue, T &result) { +void fromRawValue( + const PropsParserContext &context, + RawValue const &rawValue, + T &result) { result = (T)rawValue; } template -void fromRawValue(RawValue const &rawValue, std::vector &result) { +void fromRawValue( + const PropsParserContext &context, + RawValue const &rawValue, + std::vector &result) { if (rawValue.hasType>()) { auto items = (std::vector)rawValue; auto length = items.size(); result.clear(); result.reserve(length); - for (int i = 0; i < length; i++) { + for (size_t i = 0; i < length; i++) { T itemResult; - fromRawValue(items.at(i), itemResult); + fromRawValue(context, items.at(i), itemResult); result.push_back(itemResult); } return; @@ -42,12 +49,13 @@ void fromRawValue(RawValue const &rawValue, std::vector &result) { result.clear(); result.reserve(1); T itemResult; - fromRawValue(rawValue, itemResult); + fromRawValue(context, rawValue, itemResult); result.push_back(itemResult); } template void fromRawValue( + const PropsParserContext &context, RawValue const &rawValue, std::vector> &result) { if (rawValue.hasType>>()) { @@ -57,7 +65,7 @@ void fromRawValue( result.reserve(length); for (int i = 0; i < length; i++) { T itemResult; - fromRawValue(items.at(i), itemResult); + fromRawValue(context, items.at(i), itemResult); result.push_back(itemResult); } return; @@ -67,12 +75,13 @@ void fromRawValue( result.clear(); result.reserve(1); T itemResult; - fromRawValue(rawValue, itemResult); + fromRawValue(context, rawValue, itemResult); result.push_back(itemResult); } template T convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, char const *name, T const &sourceValue, @@ -92,12 +101,13 @@ T convertRawProp( } T result; - fromRawValue(*rawValue, result); + fromRawValue(context, *rawValue, result); return result; } template static better::optional convertRawProp( + const PropsParserContext &context, RawProps const &rawProps, char const *name, better::optional const &sourceValue, @@ -117,7 +127,7 @@ static better::optional convertRawProp( } T result; - fromRawValue(*rawValue, result); + fromRawValue(context, *rawValue, result); return better::optional{result}; } diff --git a/android/ReactCommon/react/renderer/core/tests/ComponentDescriptorTest.cpp b/android/ReactCommon/react/renderer/core/tests/ComponentDescriptorTest.cpp index 58baadffb321a..35a12911b250a 100644 --- a/android/ReactCommon/react/renderer/core/tests/ComponentDescriptorTest.cpp +++ b/android/ReactCommon/react/renderer/core/tests/ComponentDescriptorTest.cpp @@ -7,6 +7,8 @@ #include +#include + #include "TestComponent.h" using namespace facebook::react; @@ -21,8 +23,11 @@ TEST(ComponentDescriptorTest, createShadowNode) { EXPECT_STREQ(descriptor->getComponentName(), TestShadowNode::Name()); EXPECT_STREQ(descriptor->getComponentName(), "Test"); + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc")); - SharedProps props = descriptor->cloneProps(nullptr, raw); + SharedProps props = descriptor->cloneProps(parserContext, nullptr, raw); auto family = descriptor->createFamily( ShadowNodeFamilyFragment{ @@ -52,8 +57,11 @@ TEST(ComponentDescriptorTest, cloneShadowNode) { std::make_shared( ComponentDescriptorParameters{eventDispatcher, nullptr, nullptr}); + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc")); - SharedProps props = descriptor->cloneProps(nullptr, raw); + SharedProps props = descriptor->cloneProps(parserContext, nullptr, raw); auto family = descriptor->createFamily( ShadowNodeFamilyFragment{ /* .tag = */ 9, @@ -73,7 +81,8 @@ TEST(ComponentDescriptorTest, cloneShadowNode) { EXPECT_EQ(cloned->getSurfaceId(), 1); EXPECT_STREQ(cloned->getProps()->nativeId.c_str(), "abc"); - auto clonedButSameProps = descriptor->cloneProps(props, RawProps()); + auto clonedButSameProps = + descriptor->cloneProps(parserContext, props, RawProps()); EXPECT_NE(clonedButSameProps, props); } @@ -83,8 +92,11 @@ TEST(ComponentDescriptorTest, appendChild) { std::make_shared( ComponentDescriptorParameters{eventDispatcher, nullptr, nullptr}); + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc")); - SharedProps props = descriptor->cloneProps(nullptr, raw); + SharedProps props = descriptor->cloneProps(parserContext, nullptr, raw); auto family1 = descriptor->createFamily( ShadowNodeFamilyFragment{ /* .tag = */ 1, diff --git a/android/ReactCommon/react/renderer/core/tests/ConcreteShadowNodeTest.cpp b/android/ReactCommon/react/renderer/core/tests/ConcreteShadowNodeTest.cpp index de48a4b6e7b56..0b2d025d08ad4 100644 --- a/android/ReactCommon/react/renderer/core/tests/ConcreteShadowNodeTest.cpp +++ b/android/ReactCommon/react/renderer/core/tests/ConcreteShadowNodeTest.cpp @@ -26,7 +26,7 @@ TEST(ConcreteShadowNodeTest, testSetStateData) { auto shadowNode = builder.build(element); - shadowNode->setStateData({{10, 11}, {{21, 22}, {301, 302}}}); + shadowNode->setStateData({{10, 11}, {{21, 22}, {301, 302}}, 0}); EXPECT_NE( shadowNode->getState(), shadowNode->getFamily().getMostRecentState()); diff --git a/android/ReactCommon/react/renderer/core/tests/DynamicPropsUtilitiesTest.cpp b/android/ReactCommon/react/renderer/core/tests/DynamicPropsUtilitiesTest.cpp new file mode 100644 index 0000000000000..b6666ad0842a6 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/tests/DynamicPropsUtilitiesTest.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +using namespace folly; +using namespace facebook::react; + +/* + Tests that verify expected behaviour from `folly::dynamic::merge_patch`. + `merge_patch` is used for props forwarding on Android to enable Background + Executor and will be removed once JNI layer is reimplmeneted. + */ +TEST(DynamicPropsUtilitiesTest, handleNestedObjects) { + dynamic map1 = dynamic::object; + map1["style"] = dynamic::object("backgroundColor", "red"); + + dynamic map2 = dynamic::object; + map2["style"] = dynamic::object("backgroundColor", "blue")("color", "black"); + map2["height"] = 100; + + auto result = mergeDynamicProps(map1, map2); + + EXPECT_TRUE(result["style"].isObject()); + EXPECT_TRUE(result["style"]["backgroundColor"].isString()); + EXPECT_TRUE(result["style"]["color"].isString()); + EXPECT_TRUE(result["height"].isInt()); + + EXPECT_EQ(result["style"]["backgroundColor"].asString(), "blue"); + EXPECT_EQ(result["style"]["color"], "black"); + EXPECT_EQ(result["height"], 100); +} + +TEST(DynamicPropsUtilitiesTest, handleEmptyObject) { + dynamic map1 = dynamic::object; + + dynamic map2 = dynamic::object; + map2["height"] = 100; + + auto result = mergeDynamicProps(map1, map2); + + EXPECT_TRUE(result["height"].isInt()); + EXPECT_EQ(result["height"], 100); + + result = mergeDynamicProps(map1, map2); + + EXPECT_TRUE(result["height"].isInt()); + EXPECT_EQ(result["height"], 100); +} + +TEST(DynamicPropsUtilitiesTest, handleNull) { + dynamic map1 = dynamic::object; + map1["height"] = 100; + + dynamic map2 = dynamic::object; + map2["height"] = nullptr; + + auto result = mergeDynamicProps(map1, map2); + + EXPECT_TRUE(result["height"].isNull()); +} diff --git a/android/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp b/android/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp new file mode 100644 index 0000000000000..268a6a0aca903 --- /dev/null +++ b/android/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include +#include + +#include + +namespace facebook::react { + +class EventQueueProcessorTest : public testing::Test { + protected: + void SetUp() override { + runtime_ = facebook::hermes::makeHermesRuntime(); + + auto eventPipe = [this]( + jsi::Runtime &runtime, + const EventTarget *eventTarget, + const std::string &type, + ReactEventPriority priority, + const ValueFactory &payloadFactory) { + eventTypes_.push_back(type); + eventPriorities_.push_back(priority); + }; + + auto dummyStatePipe = [](StateUpdate const &stateUpdate) {}; + + eventProcessor_ = + std::make_unique(eventPipe, dummyStatePipe); + } + + std::unique_ptr runtime_; + std::unique_ptr eventProcessor_; + std::vector eventTypes_; + std::vector eventPriorities_; + ValueFactory dummyValueFactory_; +}; + +TEST_F(EventQueueProcessorTest, singleUnspecifiedEvent) { + eventProcessor_->flushEvents( + *runtime_, + {RawEvent( + "my type", + dummyValueFactory_, + nullptr, + RawEvent::Category::Unspecified)}); + + EXPECT_EQ(eventPriorities_.size(), 1); + EXPECT_EQ(eventTypes_[0], "my type"); + EXPECT_EQ(eventPriorities_[0], ReactEventPriority::Discrete); +} + +TEST_F(EventQueueProcessorTest, continiousEvent) { + eventProcessor_->flushEvents( + *runtime_, + {RawEvent( + "touchStart", + dummyValueFactory_, + nullptr, + RawEvent::Category::ContinuousStart), + RawEvent( + "touchMove", + dummyValueFactory_, + nullptr, + RawEvent::Category::Unspecified), + RawEvent( + "touchEnd", + dummyValueFactory_, + nullptr, + RawEvent::Category::ContinuousEnd), + RawEvent( + "custom event", + dummyValueFactory_, + nullptr, + RawEvent::Category::Unspecified)}); + + EXPECT_EQ(eventPriorities_.size(), 4); + + EXPECT_EQ(eventTypes_[0], "touchStart"); + EXPECT_EQ(eventPriorities_[0], ReactEventPriority::Discrete); + + EXPECT_EQ(eventTypes_[1], "touchMove"); + EXPECT_EQ(eventPriorities_[1], ReactEventPriority::Default); + + EXPECT_EQ(eventTypes_[2], "touchEnd"); + EXPECT_EQ(eventPriorities_[2], ReactEventPriority::Discrete); + + EXPECT_EQ(eventTypes_[3], "custom event"); + EXPECT_EQ(eventPriorities_[3], ReactEventPriority::Discrete); +} + +TEST_F(EventQueueProcessorTest, alwaysContinuousEvent) { + eventProcessor_->flushEvents( + *runtime_, + { + RawEvent( + "onScroll", + dummyValueFactory_, + nullptr, + RawEvent::Category::Continuous), + }); + + EXPECT_EQ(eventPriorities_.size(), 1); + + EXPECT_EQ(eventTypes_[0], "onScroll"); + EXPECT_EQ(eventPriorities_[0], ReactEventPriority::Default); +} + +TEST_F(EventQueueProcessorTest, alwaysDiscreteEvent) { + eventProcessor_->flushEvents( + *runtime_, + { + RawEvent( + "onChange", + dummyValueFactory_, + nullptr, + RawEvent::Category::Discrete), + }); + + EXPECT_EQ(eventPriorities_.size(), 1); + + EXPECT_EQ(eventTypes_[0], "onChange"); + EXPECT_EQ(eventPriorities_[0], ReactEventPriority::Discrete); +} + +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/core/tests/LayoutableShadowNodeTest.cpp b/android/ReactCommon/react/renderer/core/tests/LayoutableShadowNodeTest.cpp index 8e02861ae79c9..c6dc5f68b5bfa 100644 --- a/android/ReactCommon/react/renderer/core/tests/LayoutableShadowNodeTest.cpp +++ b/android/ReactCommon/react/renderer/core/tests/LayoutableShadowNodeTest.cpp @@ -52,7 +52,8 @@ TEST(LayoutableShadowNodeTest, relativeLayoutMetrics) { auto parentShadowNode = builder.build(element); auto relativeLayoutMetrics = - childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + childShadowNode->getFamily(), *parentShadowNode, {}); // A is a parent to B, A has origin {10, 10}, B has origin {10, 10}. // B's relative origin to A should be {10, 10}. @@ -104,13 +105,14 @@ TEST(LayoutableShadowNodeTest, contentOriginOffset) { auto parentShadowNode = builder.build(element); auto relativeLayoutMetrics = - childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + childShadowNode->getFamily(), *parentShadowNode, {}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 10); - relativeLayoutMetrics = - childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {false}); + relativeLayoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics( + childShadowNode->getFamily(), *parentShadowNode, {false}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20); @@ -157,7 +159,8 @@ TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedNode) { auto parentShadowNode = builder.build(element); auto relativeLayoutMetrics = - childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + childShadowNode->getFamily(), *parentShadowNode, {}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 35); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 70); @@ -222,7 +225,8 @@ TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) { auto parentShadowNode = builder.build(element); auto relativeLayoutMetrics = - childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + childShadowNode->getFamily(), *parentShadowNode, {}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 45); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 45); @@ -254,7 +258,8 @@ TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode) { auto shadowNode = builder.build(element); auto relativeLayoutMetrics = - shadowNode->getRelativeLayoutMetrics(*shadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + shadowNode->getFamily(), *shadowNode, {}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 0); @@ -290,7 +295,8 @@ TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameTransformedNode) { auto shadowNode = builder.build(element); auto relativeLayoutMetrics = - shadowNode->getRelativeLayoutMetrics(*shadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + shadowNode->getFamily(), *shadowNode, {}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 0); @@ -332,7 +338,8 @@ TEST(LayoutableShadowNodeTest, relativeLayourMetricsOnClonedNode) { parentShadowNode->replaceChild(*childShadowNode, clonedChildShadowNode); auto newRelativeLayoutMetrics = - childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {}); + LayoutableShadowNode::computeRelativeLayoutMetrics( + childShadowNode->getFamily(), *parentShadowNode, {}); EXPECT_EQ(newRelativeLayoutMetrics.frame.size.width, 50); EXPECT_EQ(newRelativeLayoutMetrics.frame.size.height, 60); } @@ -379,7 +386,7 @@ TEST( auto parentShadowNode = builder.build(element); - auto relativeLayoutMetrics = childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {}); + auto relativeLayoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics(childShadowNode->getFamily(), *parentShadowNode, {}); // relativeLayoutMetrics do not include offsset of nodeAA_ because it is a // RootKindNode. @@ -410,13 +417,15 @@ TEST(LayoutableShadowNodeTest, includeViewportOffset) { // `includeViewportOffset` has to work with `includeTransform` enabled and // disabled. - auto layoutMetrics = viewShadowNode->getRelativeLayoutMetrics( + auto layoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics( + viewShadowNode->getFamily(), *rootShadowNode, {/* includeTransform = */ false, /* includeViewportOffset = */ true}); EXPECT_EQ(layoutMetrics.frame.origin.x, 10); EXPECT_EQ(layoutMetrics.frame.origin.y, 20); - layoutMetrics = viewShadowNode->getRelativeLayoutMetrics( + layoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics( + viewShadowNode->getFamily(), *rootShadowNode, {/* includeTransform = */ true, /* includeViewportOffset = */ true}); EXPECT_EQ(layoutMetrics.frame.origin.x, 10); diff --git a/android/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp b/android/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp index 48a8726662b64..e54b623c9984b 100644 --- a/android/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp +++ b/android/ReactCommon/react/renderer/core/tests/RawPropsTest.cpp @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include #include @@ -20,9 +22,11 @@ class PropsSingleFloat : public Props { public: PropsSingleFloat() = default; PropsSingleFloat( + const PropsParserContext &context, const PropsSingleFloat &sourceProps, const RawProps &rawProps) : floatValue(convertRawProp( + context, rawProps, "floatValue", sourceProps.floatValue, @@ -36,9 +40,11 @@ class PropsSingleDouble : public Props { public: PropsSingleDouble() = default; PropsSingleDouble( + const PropsParserContext &context, const PropsSingleDouble &sourceProps, const RawProps &rawProps) : doubleValue(convertRawProp( + context, rawProps, "doubleValue", sourceProps.doubleValue, @@ -51,9 +57,16 @@ class PropsSingleDouble : public Props { class PropsSingleInt : public Props { public: PropsSingleInt() = default; - PropsSingleInt(const PropsSingleInt &sourceProps, const RawProps &rawProps) - : intValue( - convertRawProp(rawProps, "intValue", sourceProps.intValue, 17)) {} + PropsSingleInt( + const PropsParserContext &context, + const PropsSingleInt &sourceProps, + const RawProps &rawProps) + : intValue(convertRawProp( + context, + rawProps, + "intValue", + sourceProps.intValue, + 17)) {} private: const int intValue{17}; @@ -63,26 +76,35 @@ class PropsPrimitiveTypes : public Props { public: PropsPrimitiveTypes() = default; PropsPrimitiveTypes( + const PropsParserContext &context, const PropsPrimitiveTypes &sourceProps, const RawProps &rawProps) - : intValue( - convertRawProp(rawProps, "intValue", sourceProps.intValue, 17)), + : intValue(convertRawProp( + context, + rawProps, + "intValue", + sourceProps.intValue, + 17)), doubleValue(convertRawProp( + context, rawProps, "doubleValue", sourceProps.doubleValue, 17.56)), floatValue(convertRawProp( + context, rawProps, "floatValue", sourceProps.floatValue, 56.75)), stringValue(convertRawProp( + context, rawProps, "stringValue", sourceProps.stringValue, "")), boolValue(convertRawProp( + context, rawProps, "boolValue", sourceProps.boolValue, @@ -100,9 +122,11 @@ class PropsMultiLookup : public Props { public: PropsMultiLookup() = default; PropsMultiLookup( + const PropsParserContext &context, const PropsMultiLookup &sourceProps, const RawProps &rawProps) : floatValue(convertRawProp( + context, rawProps, "floatValue", sourceProps.floatValue, @@ -111,7 +135,12 @@ class PropsMultiLookup : public Props { // pattern that does occur a lot: nested structs that access props we // have already accessed populating Props derivedFloatValue( - convertRawProp(rawProps, "floatValue", sourceProps.floatValue, 40) * + convertRawProp( + context, + rawProps, + "floatValue", + sourceProps.floatValue, + 40) * 2) {} const float floatValue{17.5}; @@ -119,12 +148,15 @@ class PropsMultiLookup : public Props { }; TEST(RawPropsTest, handleProps) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc")); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); - auto props = std::make_shared(Props(), raw); + auto props = std::make_shared(parserContext, Props(), raw); // Props are not sealed after applying raw props. EXPECT_FALSE(props->getSealed()); @@ -133,10 +165,13 @@ TEST(RawPropsTest, handleProps) { } TEST(RawPropsTest, handleRawPropsSingleString) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc")); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); std::string value = (std::string)*raw.at("nativeID", nullptr, nullptr); @@ -144,11 +179,14 @@ TEST(RawPropsTest, handleRawPropsSingleString) { } TEST(RawPropsTest, handleRawPropsSingleFloat) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("floatValue", (float)42.42)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); float value = (float)*raw.at("floatValue", nullptr, nullptr); @@ -156,11 +194,14 @@ TEST(RawPropsTest, handleRawPropsSingleFloat) { } TEST(RawPropsTest, handleRawPropsSingleDouble) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("doubleValue", (double)42.42)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); double value = (double)*raw.at("doubleValue", nullptr, nullptr); @@ -168,10 +209,13 @@ TEST(RawPropsTest, handleRawPropsSingleDouble) { } TEST(RawPropsTest, handleRawPropsSingleInt) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42.42)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); int value = (int)*raw.at("intValue", nullptr, nullptr); @@ -179,10 +223,13 @@ TEST(RawPropsTest, handleRawPropsSingleInt) { } TEST(RawPropsTest, handleRawPropsSingleIntGetManyTimes) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42.42)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); @@ -190,13 +237,17 @@ TEST(RawPropsTest, handleRawPropsSingleIntGetManyTimes) { } TEST(RawPropsTest, handleRawPropsPrimitiveTypes) { - const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42)( - "doubleValue", (double)17.42)("floatValue", (float)66.67)( - "stringValue", "helloworld")("boolValue", true)); + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + const auto &raw = RawProps( + folly::dynamic::object("intValue", (int)42)("doubleValue", (double)17.42)( + "floatValue", + (float)66.67)("stringValue", "helloworld")("boolValue", true)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); EXPECT_NEAR((double)*raw.at("doubleValue", nullptr, nullptr), 17.42, 0.0001); @@ -208,13 +259,17 @@ TEST(RawPropsTest, handleRawPropsPrimitiveTypes) { } TEST(RawPropsTest, handleRawPropsPrimitiveTypesGetTwice) { - const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42)( - "doubleValue", (double)17.42)("floatValue", (float)66.67)( - "stringValue", "helloworld")("boolValue", true)); + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + const auto &raw = RawProps( + folly::dynamic::object("intValue", (int)42)("doubleValue", (double)17.42)( + "floatValue", + (float)66.67)("stringValue", "helloworld")("boolValue", true)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); EXPECT_NEAR((double)*raw.at("doubleValue", nullptr, nullptr), 17.42, 0.0001); @@ -234,13 +289,17 @@ TEST(RawPropsTest, handleRawPropsPrimitiveTypesGetTwice) { } TEST(RawPropsTest, handleRawPropsPrimitiveTypesGetOutOfOrder) { - const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42)( - "doubleValue", (double)17.42)("floatValue", (float)66.67)( - "stringValue", "helloworld")("boolValue", true)); + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + const auto &raw = RawProps( + folly::dynamic::object("intValue", (int)42)("doubleValue", (double)17.42)( + "floatValue", + (float)66.67)("stringValue", "helloworld")("boolValue", true)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); EXPECT_NEAR((double)*raw.at("doubleValue", nullptr, nullptr), 17.42, 0.0001); @@ -260,11 +319,14 @@ TEST(RawPropsTest, handleRawPropsPrimitiveTypesGetOutOfOrder) { } TEST(RawPropsTest, handleRawPropsPrimitiveTypesIncomplete) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); EXPECT_EQ(raw.at("doubleValue", nullptr, nullptr), nullptr); @@ -275,13 +337,16 @@ TEST(RawPropsTest, handleRawPropsPrimitiveTypesIncomplete) { EXPECT_EQ((int)*raw.at("intValue", nullptr, nullptr), 42); } -#ifndef NDEBUG +#ifdef REACT_NATIVE_DEBUG TEST(RawPropsTest, handleRawPropsPrimitiveTypesIncorrectLookup) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("intValue", (int)42)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); // Before D18662135, looking up an invalid key would trigger // an infinite loop. This is out of contract, so we should only @@ -292,12 +357,16 @@ TEST(RawPropsTest, handleRawPropsPrimitiveTypesIncorrectLookup) { #endif TEST(RawPropsTest, handlePropsMultiLookup) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + const auto &raw = RawProps(folly::dynamic::object("floatValue", (float)10.0)); auto parser = RawPropsParser(); parser.prepare(); - raw.parse(parser); + raw.parse(parser, parserContext); - auto props = std::make_shared(PropsMultiLookup(), raw); + auto props = std::make_shared( + parserContext, PropsMultiLookup(), raw); // Props are not sealed after applying raw props. EXPECT_FALSE(props->getSealed()); diff --git a/android/ReactCommon/react/renderer/core/tests/TestComponent.h b/android/ReactCommon/react/renderer/core/tests/TestComponent.h index 9943d78a031b4..a396f1ba6217b 100644 --- a/android/ReactCommon/react/renderer/core/tests/TestComponent.h +++ b/android/ReactCommon/react/renderer/core/tests/TestComponent.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -35,8 +36,11 @@ class TestProps : public ViewProps { public: TestProps() = default; - TestProps(const TestProps &sourceProps, const RawProps &rawProps) - : ViewProps(sourceProps, rawProps) {} + TestProps( + const PropsParserContext &context, + const TestProps &sourceProps, + const RawProps &rawProps) + : ViewProps(context, sourceProps, rawProps) {} }; using SharedTestProps = std::shared_ptr; @@ -45,11 +49,11 @@ class TestShadowNode; using SharedTestShadowNode = std::shared_ptr; -class TestShadowNode : public ConcreteViewShadowNode< - TestComponentName, - TestProps, - ViewEventEmitter, - TestState> { +class TestShadowNode final : public ConcreteViewShadowNode< + TestComponentName, + TestProps, + ViewEventEmitter, + TestState> { public: using ConcreteViewShadowNode::ConcreteViewShadowNode; diff --git a/android/ReactCommon/react/renderer/core/tests/traitCastTest.cpp b/android/ReactCommon/react/renderer/core/tests/traitCastTest.cpp index f4af650f4993f..e68e4b1666335 100644 --- a/android/ReactCommon/react/renderer/core/tests/traitCastTest.cpp +++ b/android/ReactCommon/react/renderer/core/tests/traitCastTest.cpp @@ -47,6 +47,9 @@ TEST(traitCastTest, testOne) { auto rootShadowNode = builder.build(element); + std::shared_ptr shadowNodeForRawTextShadowNode{rawTextShadowNode}; + std::shared_ptr shadowNodeForTextShadowNode{textShadowNode}; + // Casting `nullptr` returns `nullptrs`. EXPECT_FALSE(traitCast(nullptr)); EXPECT_FALSE(traitCast(nullptr)); @@ -102,4 +105,32 @@ TEST(traitCastTest, testOne) { traitCast(*rawTextShadowNode), ""); EXPECT_DEATH_IF_SUPPORTED( traitCast(*rawTextShadowNode), ""); + + // trait cast to `RawTextShadowNode` works on `RawTextShadowNode` + // and not on TextShadowNode or ViewShadowNode + EXPECT_TRUE(traitCast( + shadowNodeForRawTextShadowNode.get())); + EXPECT_NO_FATAL_FAILURE( + traitCast(*shadowNodeForRawTextShadowNode)); + EXPECT_FALSE( + traitCast(shadowNodeForTextShadowNode.get())); + EXPECT_DEATH_IF_SUPPORTED( + traitCast(*shadowNodeForTextShadowNode), ""); + EXPECT_FALSE(traitCast(viewShadowNode.get())); + EXPECT_DEATH_IF_SUPPORTED( + traitCast(*viewShadowNode), ""); + + // trait cast to `TextShadowNode` works on `TextShadowNode` + // and not on RawTextShadowNode or ViewShadowNode + EXPECT_TRUE( + traitCast(shadowNodeForTextShadowNode.get())); + EXPECT_NO_FATAL_FAILURE( + traitCast(*shadowNodeForTextShadowNode)); + EXPECT_FALSE( + traitCast(shadowNodeForRawTextShadowNode.get())); + EXPECT_DEATH_IF_SUPPORTED( + traitCast(*shadowNodeForRawTextShadowNode), ""); + EXPECT_FALSE(traitCast(viewShadowNode.get())); + EXPECT_DEATH_IF_SUPPORTED( + traitCast(*viewShadowNode), ""); } diff --git a/android/ReactCommon/react/renderer/debug/Android.mk b/android/ReactCommon/react/renderer/debug/Android.mk index 6a86e43ab318a..e856283cc6a4f 100644 --- a/android/ReactCommon/react/renderer/debug/Android.mk +++ b/android/ReactCommon/react/renderer/debug/Android.mk @@ -20,7 +20,7 @@ LOCAL_SHARED_LIBRARIES := libfolly_json LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) diff --git a/android/ReactCommon/react/renderer/debug/BUCK b/android/ReactCommon/react/renderer/debug/BUCK index 9950f239cc557..e4b2ee2303588 100644 --- a/android/ReactCommon/react/renderer/debug/BUCK +++ b/android/ReactCommon/react/renderer/debug/BUCK @@ -1,8 +1,4 @@ load("@fbsource//tools/build_defs:platform_defs.bzl", "CXX") -load( - "@fbsource//tools/build_defs/apple:flag_defs.bzl", - "get_preprocessor_flags_for_build_mode", -) load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -10,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -34,12 +31,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/debug", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -58,6 +49,7 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", react_native_xplat_target("better:better"), + react_native_xplat_target("react/debug:debug"), ], ) @@ -68,7 +60,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/debug/DebugStringConvertible.h b/android/ReactCommon/react/renderer/debug/DebugStringConvertible.h index de8ce9c6e5385..8833211cd2599 100644 --- a/android/ReactCommon/react/renderer/debug/DebugStringConvertible.h +++ b/android/ReactCommon/react/renderer/debug/DebugStringConvertible.h @@ -13,25 +13,11 @@ #include #include +#include "flags.h" + namespace facebook { namespace react { -#ifndef NDEBUG -#define RN_DEBUG_STRING_CONVERTIBLE 1 -#endif - -// To Debug Yoga layout, uncomment the following line. -// #define RN_DEBUG_YOGA_LOGGER 1 -// -// Additional logging can be enabled editing yoga.cpp (e.g. gPrintChanges, -// gPrintSkips) - -// To Debug introspection of RN Shadow tree, uncomment the following line: -// #define RN_SHADOW_TREE_INTROSPECTION 1 - -// To enable asserts (crashing) when checking stub trees -// #define RN_VALIDATE_SHADOW_TREE_STUB 1 - #if RN_DEBUG_STRING_CONVERTIBLE class DebugStringConvertible; @@ -206,16 +192,16 @@ std::string getDebugChildrenDescription( return ""; } - auto trailing = options.format ? std::string{"\n"} : std::string{""}; + auto separator = options.format ? std::string{"\n"} : std::string{""}; auto childrenString = std::string{""}; options.depth++; for (auto child : getDebugChildren(object, options)) { - childrenString += getDebugDescription(child, options) + trailing; + childrenString += getDebugDescription(child, options) + separator; } - if (!childrenString.empty() && !trailing.empty()) { - // Removing trailing fragment. + if (!childrenString.empty() && !separator.empty()) { + // Removing separator fragment. childrenString.erase(childrenString.end() - 1); } @@ -244,16 +230,16 @@ std::string getDebugDescription( auto childrenString = getDebugChildrenDescription(object, options); auto propsString = getDebugPropsDescription(object, options); - auto leading = + auto prefix = options.format ? std::string(options.depth * 2, ' ') : std::string{""}; - auto trailing = options.format ? std::string{"\n"} : std::string{""}; + auto separator = options.format ? std::string{"\n"} : std::string{""}; - return leading + "<" + nameString + + return prefix + "<" + nameString + (valueString.empty() ? "" : "=" + valueString) + (propsString.empty() ? "" : " " + propsString) + (childrenString.empty() ? "/>" - : ">" + trailing + childrenString + trailing + - leading + ""); + : ">" + separator + childrenString + separator + + prefix + ""); } /* diff --git a/android/ReactCommon/react/renderer/debug/SystraceSection.h b/android/ReactCommon/react/renderer/debug/SystraceSection.h index a21cd36b2b726..603c823b4e513 100644 --- a/android/ReactCommon/react/renderer/debug/SystraceSection.h +++ b/android/ReactCommon/react/renderer/debug/SystraceSection.h @@ -29,7 +29,7 @@ struct ConcreteSystraceSection { template explicit ConcreteSystraceSection( const char *name, - ConvertsToStringPiece &&... args) + ConvertsToStringPiece &&...args) : m_section(TRACE_TAG_REACT_CXX_BRIDGE, name, args...) {} private: @@ -42,7 +42,7 @@ struct DummySystraceSection { template explicit DummySystraceSection( const char *name, - ConvertsToStringPiece &&... args) {} + ConvertsToStringPiece &&...args) {} }; using SystraceSection = DummySystraceSection; #endif diff --git a/android/ReactCommon/react/renderer/debug/flags.h b/android/ReactCommon/react/renderer/debug/flags.h new file mode 100644 index 0000000000000..698c9a55fc528 --- /dev/null +++ b/android/ReactCommon/react/renderer/debug/flags.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +// +// This file contains flags that should __never__ be enabled for +// release-mode/production builds, unless explicitly noted. You can enable some +// of these for debug or local builds to assist in logging / debugging specific +// features. +// + +// Enables verbose logging for the LayoutAnimations subsystem. +//#define LAYOUT_ANIMATION_VERBOSE_LOGGING 1 + +// Logs information before running `assert` in LayoutAnimations. More useful on +// Android vs other platforms. +//#define VERBOSE_LAYOUT_ANIMATION_ASSERTS 1 + +// Enables some Shadow Tree introspection features (maintains a StubViewTree, +// and logs prev/next tree and mutations if there are any discrepancies). If you +// define this, also define `RN_DEBUG_STRING_CONVERTIBLE`. +#ifdef REACT_NATIVE_DEBUG +#define RN_SHADOW_TREE_INTROSPECTION 1 +#endif + +// This enables certain object-to-string debug conversions to be compiled. +// Enable if `RN_SHADOW_TREE_INTROSPECTION` is enabled. +#ifdef RN_SHADOW_TREE_INTROSPECTION +#define RN_DEBUG_STRING_CONVERTIBLE 1 +#endif + +// Enables *very* verbose, noisy logs in the differ. Useful for debugging +// specifically the differ, but not much else. +//#define DEBUG_LOGS_DIFFER + +// Uncomment to enable verbose StubViewTree debug logs. This ensures that errors +// are logged to console before the `assert` is fired. More useful on Android vs +// other platforms. +//#define STUB_VIEW_TREE_VERBOSE 1 + +// Verbose logging for certain Yoga-related things in the RN codebase (not Yoga +// codebase). Useful for debugging layout. +//#define RN_DEBUG_YOGA_LOGGER 1 diff --git a/android/ReactCommon/react/renderer/element/BUCK b/android/ReactCommon/react/renderer/element/BUCK index 1e2ac391114f6..6db4cf614b984 100644 --- a/android/ReactCommon/react/renderer/element/BUCK +++ b/android/ReactCommon/react/renderer/element/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -31,12 +31,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/element", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -72,7 +66,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/element/ComponentBuilder.cpp b/android/ReactCommon/react/renderer/element/ComponentBuilder.cpp index 242b8d854e662..c1438bb63aafd 100644 --- a/android/ReactCommon/react/renderer/element/ComponentBuilder.cpp +++ b/android/ReactCommon/react/renderer/element/ComponentBuilder.cpp @@ -45,9 +45,10 @@ ShadowNode::Unshared ComponentBuilder::build( *family, elementFragment.stateCallback()); constShadowNode = componentDescriptor.cloneShadowNode( *constShadowNode, - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - ShadowNodeFragment::childrenPlaceholder(), - newState}); + ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + ShadowNodeFragment::childrenPlaceholder(), + newState}); } auto shadowNode = std::const_pointer_cast(constShadowNode); diff --git a/android/ReactCommon/react/renderer/graphics/Android.mk b/android/ReactCommon/react/renderer/graphics/Android.mk index 675a27973a5f9..94e3adbce79a1 100644 --- a/android/ReactCommon/react/renderer/graphics/Android.mk +++ b/android/ReactCommon/react/renderer/graphics/Android.mk @@ -11,19 +11,23 @@ LOCAL_MODULE := react_render_graphics LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp $(LOCAL_PATH)/platform/cxx/react/renderer/graphics/*.cpp) -LOCAL_SHARED_LIBRARIES := libfolly_json +LOCAL_SHARED_LIBRARIES := libfolly_json libreact_debug libfb libfbjni libfolly_json glog LOCAL_STATIC_LIBRARIES := -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/platform/cxx/ +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/platform/cxx/ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/platform/cxx/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) +$(call import-module,glog) +$(call import-module,fbjni) +$(call import-module,fb) $(call import-module,folly) +$(call import-module,react/debug) diff --git a/android/ReactCommon/react/renderer/graphics/BUCK b/android/ReactCommon/react/renderer/graphics/BUCK index 87064ce89c043..34748db242729 100644 --- a/android/ReactCommon/react/renderer/graphics/BUCK +++ b/android/ReactCommon/react/renderer/graphics/BUCK @@ -1,12 +1,14 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "CXX", + "FBJNI_TARGET", "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -34,12 +36,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/graphics", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_exported_headers = subdir_glob( [ ("platform/cxx/react/renderer/graphics", "**/*.h"), @@ -51,23 +47,32 @@ rn_xplat_cxx_library( "platform/cxx/react/renderer/graphics/**/*.cpp", ], ), + fbandroid_allow_jni_merging = True, + fbandroid_deps = [ + FBJNI_TARGET, + react_native_target("jni/react/jni:jni"), + ], fbandroid_exported_headers = subdir_glob( [ + ("platform/android/react/renderer/graphics", "**/*.h"), ("platform/cxx/react/renderer/graphics", "**/*.h"), ], + exclude = ["platform/cxx/react/renderer/graphics/PlatformColorParser.h"], prefix = "react/renderer/graphics", ), fbandroid_srcs = glob( [ "platform/cxx/react/renderer/graphics/**/*.cpp", + "platform/android/react/renderer/graphics/**/*.cpp", ], + exclude = ["platform/cxx/react/renderer/graphics/PlatformColorParser.h"], ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, ios_deps = [ - "//xplat/js:RCTImage", "//xplat/js/react-native-github:RCTCxxBridge", + "//xplat/js/react-native-github:RCTImage", ], ios_exported_headers = subdir_glob( [ @@ -96,6 +101,7 @@ rn_xplat_cxx_library( tests = [":tests"], visibility = ["PUBLIC"], deps = [ + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("better:better"), "//third-party/glog:glog", "//xplat/fbsystrace:fbsystrace", @@ -112,7 +118,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/graphics/React-graphics.podspec b/android/ReactCommon/react/renderer/graphics/React-graphics.podspec index aebbc2cd29326..bb4913623839c 100644 --- a/android/ReactCommon/react/renderer/graphics/React-graphics.podspec +++ b/android/ReactCommon/react/renderer/graphics/React-graphics.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,16 +27,16 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "9.0", :tvos => "10.0" } + s.platforms = { :ios => "11.0", :tvos => "11.0" } s.source = source - s.library = "stdc++" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.source_files = "**/*.{m,mm,cpp,h}" s.exclude_files = "tests", "platform/android", "platform/cxx" s.header_dir = "react/renderer/graphics" - s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/../../../\" \"$(PODS_ROOT)/RCT-Folly\"" } + s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_TARGET_SRCROOT)/../../../\" \"$(PODS_ROOT)/RCT-Folly\"" } s.dependency "RCT-Folly/Fabric", folly_version + s.dependency "React-Core/Default", version end diff --git a/android/ReactCommon/react/renderer/graphics/Rect.h b/android/ReactCommon/react/renderer/graphics/Rect.h index 78b29c4a3b76e..19db0b44b6ac1 100644 --- a/android/ReactCommon/react/renderer/graphics/Rect.h +++ b/android/ReactCommon/react/renderer/graphics/Rect.h @@ -94,9 +94,10 @@ struct Rect { rightBottomPoint.y = std::max(rightBottomPoint.y, c.y); rightBottomPoint.y = std::max(rightBottomPoint.y, d.y); - return {leftTopPoint, - {rightBottomPoint.x - leftTopPoint.x, - rightBottomPoint.y - leftTopPoint.y}}; + return { + leftTopPoint, + {rightBottomPoint.x - leftTopPoint.x, + rightBottomPoint.y - leftTopPoint.y}}; } }; diff --git a/android/ReactCommon/react/renderer/graphics/RectangleCorners.h b/android/ReactCommon/react/renderer/graphics/RectangleCorners.h index c3932cd67620e..771dc9f24b8e0 100644 --- a/android/ReactCommon/react/renderer/graphics/RectangleCorners.h +++ b/android/ReactCommon/react/renderer/graphics/RectangleCorners.h @@ -57,8 +57,8 @@ namespace std { template struct hash> { - size_t operator()(facebook::react::RectangleCorners const &corners) const - noexcept { + size_t operator()( + facebook::react::RectangleCorners const &corners) const noexcept { return folly::hash::hash_combine( 0, corners.topLeft, diff --git a/android/ReactCommon/react/renderer/graphics/RectangleEdges.h b/android/ReactCommon/react/renderer/graphics/RectangleEdges.h index b99b8645576a2..616abe07e7b25 100644 --- a/android/ReactCommon/react/renderer/graphics/RectangleEdges.h +++ b/android/ReactCommon/react/renderer/graphics/RectangleEdges.h @@ -45,20 +45,22 @@ template RectangleEdges operator+( RectangleEdges const &lhs, RectangleEdges const &rhs) noexcept { - return RectangleEdges{lhs.left + rhs.left, - lhs.top + rhs.top, - lhs.right + rhs.right, - lhs.bottom + rhs.bottom}; + return RectangleEdges{ + lhs.left + rhs.left, + lhs.top + rhs.top, + lhs.right + rhs.right, + lhs.bottom + rhs.bottom}; } template RectangleEdges operator-( RectangleEdges const &lhs, RectangleEdges const &rhs) noexcept { - return RectangleEdges{lhs.left - rhs.left, - lhs.top - rhs.top, - lhs.right - rhs.right, - lhs.bottom - rhs.bottom}; + return RectangleEdges{ + lhs.left - rhs.left, + lhs.top - rhs.top, + lhs.right - rhs.right, + lhs.bottom - rhs.bottom}; } /* @@ -70,9 +72,10 @@ using EdgeInsets = RectangleEdges; * Adjusts a rectangle by the given edge insets. */ inline Rect insetBy(Rect const &rect, EdgeInsets const &insets) noexcept { - return Rect{{rect.origin.x + insets.left, rect.origin.y + insets.top}, - {rect.size.width - insets.left - insets.right, - rect.size.height - insets.top - insets.bottom}}; + return Rect{ + {rect.origin.x + insets.left, rect.origin.y + insets.top}, + {rect.size.width - insets.left - insets.right, + rect.size.height - insets.top - insets.bottom}}; } } // namespace react @@ -82,8 +85,8 @@ namespace std { template struct hash> { - size_t operator()(facebook::react::RectangleEdges const &edges) const - noexcept { + size_t operator()( + facebook::react::RectangleEdges const &edges) const noexcept { return folly::hash::hash_combine( 0, edges.left, edges.right, edges.top, edges.bottom); } diff --git a/android/ReactCommon/react/renderer/graphics/Transform.cpp b/android/ReactCommon/react/renderer/graphics/Transform.cpp index 6caea8e974986..90a588b84ad62 100644 --- a/android/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/android/ReactCommon/react/renderer/graphics/Transform.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace facebook { namespace react { @@ -182,14 +183,14 @@ TransformOperation Transform::DefaultTransformOperation( } Transform Transform::Interpolate( - float animationProgress, + Float animationProgress, Transform const &lhs, Transform const &rhs) { // Iterate through operations and reconstruct an interpolated resulting // transform If at any point we hit an "Arbitrary" Transform, return at that // point Transform result = Transform::Identity(); - for (int i = 0, j = 0; + for (size_t i = 0, j = 0; i < lhs.operations.size() || j < rhs.operations.size();) { bool haveLHS = i < lhs.operations.size(); bool haveRHS = j < rhs.operations.size(); @@ -226,8 +227,8 @@ Transform Transform::Interpolate( (haveRHS && rhs.operations[j].type == type ? rhs.operations[j++] : Transform::DefaultTransformOperation(type)); - assert(type == lhsOp.type); - assert(type == rhsOp.type); + react_native_assert(type == lhsOp.type); + react_native_assert(type == rhsOp.type); result = result * Transform::FromTransformOperation(TransformOperation{ diff --git a/android/ReactCommon/react/renderer/graphics/Transform.h b/android/ReactCommon/react/renderer/graphics/Transform.h index 993f188a81552..a346c713acc42 100644 --- a/android/ReactCommon/react/renderer/graphics/Transform.h +++ b/android/ReactCommon/react/renderer/graphics/Transform.h @@ -117,7 +117,7 @@ struct Transform { * @return */ static Transform Interpolate( - float animationProgress, + Float animationProgress, Transform const &lhs, Transform const &rhs); diff --git a/android/ReactCommon/react/renderer/graphics/conversions.h b/android/ReactCommon/react/renderer/graphics/conversions.h index 6fa241a4fa394..a30d03daf644a 100644 --- a/android/ReactCommon/react/renderer/graphics/conversions.h +++ b/android/ReactCommon/react/renderer/graphics/conversions.h @@ -8,41 +8,45 @@ #pragma once #include -#include +#include +#include +#include #include #include #include +#include namespace facebook { namespace react { #pragma mark - Color -inline void fromRawValue(const RawValue &value, SharedColor &result) { - float red; - float green; - float blue; - float alpha; +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + SharedColor &result) { + ColorComponents colorComponents = {0, 0, 0, 0}; if (value.hasType()) { auto argb = (int64_t)value; auto ratio = 255.f; - alpha = ((argb >> 24) & 0xFF) / ratio; - red = ((argb >> 16) & 0xFF) / ratio; - green = ((argb >> 8) & 0xFF) / ratio; - blue = (argb & 0xFF) / ratio; + colorComponents.alpha = ((argb >> 24) & 0xFF) / ratio; + colorComponents.red = ((argb >> 16) & 0xFF) / ratio; + colorComponents.green = ((argb >> 8) & 0xFF) / ratio; + colorComponents.blue = (argb & 0xFF) / ratio; } else if (value.hasType>()) { auto items = (std::vector)value; auto length = items.size(); - assert(length == 3 || length == 4); - red = items.at(0); - green = items.at(1); - blue = items.at(2); - alpha = length == 4 ? items.at(3) : 1.0; + react_native_assert(length == 3 || length == 4); + colorComponents.red = items.at(0); + colorComponents.green = items.at(1); + colorComponents.blue = items.at(2); + colorComponents.alpha = length == 4 ? items.at(3) : 1.0f; } else { - abort(); + colorComponents = parsePlatformColor(context, value); } - result = colorFromComponents({red, green, blue, alpha}); + + result = colorFromComponents(colorComponents); } #ifdef ANDROID @@ -51,10 +55,20 @@ inline folly::dynamic toDynamic(const SharedColor &color) { ColorComponents components = colorComponentsFromColor(color); auto ratio = 255.f; return ( - ((int)(components.alpha * ratio) & 0xff) << 24 | - ((int)(components.red * ratio) & 0xff) << 16 | - ((int)(components.green * ratio) & 0xff) << 8 | - ((int)(components.blue * ratio) & 0xff)); + ((int)round(components.alpha * ratio) & 0xff) << 24 | + ((int)round(components.red * ratio) & 0xff) << 16 | + ((int)round(components.green * ratio) & 0xff) << 8 | + ((int)round(components.blue * ratio) & 0xff)); +} + +inline int toMapBuffer(const SharedColor &color) { + ColorComponents components = colorComponentsFromColor(color); + auto ratio = 255.f; + return ( + ((int)round(components.alpha * ratio) & 0xff) << 24 | + ((int)round(components.red * ratio) & 0xff) << 16 | + ((int)round(components.green * ratio) & 0xff) << 8 | + ((int)round(components.blue * ratio) & 0xff)); } #endif @@ -70,7 +84,10 @@ inline std::string toString(const SharedColor &value) { #pragma mark - Geometry -inline void fromRawValue(const RawValue &value, Point &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + Point &result) { if (value.hasType>()) { auto map = (better::map)value; for (const auto &pair : map) { @@ -83,17 +100,25 @@ inline void fromRawValue(const RawValue &value, Point &result) { return; } + react_native_assert(value.hasType>()); if (value.hasType>()) { auto array = (std::vector)value; - assert(array.size() == 2); - result = {array.at(0), array.at(1)}; - return; + react_native_assert(array.size() == 2); + if (array.size() >= 2) { + result = {array.at(0), array.at(1)}; + } else { + result = {0, 0}; + LOG(ERROR) << "Unsupported Point vector size: " << array.size(); + } + } else { + LOG(ERROR) << "Unsupported Point type"; } - - abort(); } -inline void fromRawValue(const RawValue &value, Size &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + Size &result) { if (value.hasType>()) { auto map = (better::map)value; for (const auto &pair : map) { @@ -101,22 +126,33 @@ inline void fromRawValue(const RawValue &value, Size &result) { result.width = pair.second; } else if (pair.first == "height") { result.height = pair.second; + } else { + LOG(ERROR) << "Unsupported Size map key: " << pair.first; + react_native_assert(false); } } return; } + react_native_assert(value.hasType>()); if (value.hasType>()) { auto array = (std::vector)value; - assert(array.size() == 2); - result = {array.at(0), array.at(1)}; - return; + react_native_assert(array.size() == 2); + if (array.size() >= 2) { + result = {array.at(0), array.at(1)}; + } else { + result = {0, 0}; + LOG(ERROR) << "Unsupported Size vector size: " << array.size(); + } + } else { + LOG(ERROR) << "Unsupported Size type"; } - - abort(); } -inline void fromRawValue(const RawValue &value, EdgeInsets &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + EdgeInsets &result) { if (value.hasType()) { auto number = (Float)value; result = {number, number, number, number}; @@ -133,25 +169,37 @@ inline void fromRawValue(const RawValue &value, EdgeInsets &result) { result.bottom = pair.second; } else if (pair.first == "right") { result.right = pair.second; + } else { + LOG(ERROR) << "Unsupported EdgeInsets map key: " << pair.first; + react_native_assert(false); } } return; } + react_native_assert(value.hasType>()); if (value.hasType>()) { auto array = (std::vector)value; - assert(array.size() == 4); - result = {array.at(0), array.at(1), array.at(2), array.at(3)}; - return; + react_native_assert(array.size() == 4); + if (array.size() >= 4) { + result = {array.at(0), array.at(1), array.at(2), array.at(3)}; + } else { + result = {0, 0, 0, 0}; + LOG(ERROR) << "Unsupported EdgeInsets vector size: " << array.size(); + } + } else { + LOG(ERROR) << "Unsupported EdgeInsets type"; } - - abort(); } -inline void fromRawValue(const RawValue &value, CornerInsets &result) { +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + CornerInsets &result) { if (value.hasType()) { auto number = (Float)value; result = {number, number, number, number}; + return; } if (value.hasType>()) { @@ -165,19 +213,29 @@ inline void fromRawValue(const RawValue &value, CornerInsets &result) { result.bottomLeft = pair.second; } else if (pair.first == "bottomRight") { result.bottomRight = pair.second; + } else { + LOG(ERROR) << "Unsupported CornerInsets map key: " << pair.first; + react_native_assert(false); } } return; } + react_native_assert(value.hasType>()); if (value.hasType>()) { auto array = (std::vector)value; - assert(array.size() == 4); - result = {array.at(0), array.at(1), array.at(2), array.at(3)}; - return; + react_native_assert(array.size() == 4); + if (array.size() >= 4) { + result = {array.at(0), array.at(1), array.at(2), array.at(3)}; + } else { + LOG(ERROR) << "Unsupported CornerInsets vector size: " << array.size(); + } } - abort(); + // Error case - we should only here if all other supported cases fail + // In dev we would crash on assert before this point + result = {0, 0, 0, 0}; + LOG(ERROR) << "Unsupported CornerInsets type"; } inline std::string toString(const Point &point) { diff --git a/android/ReactCommon/react/renderer/graphics/platform/android/react/renderer/graphics/PlatformColorParser.h b/android/ReactCommon/react/renderer/graphics/platform/android/react/renderer/graphics/PlatformColorParser.h new file mode 100644 index 0000000000000..950dd1ad84264 --- /dev/null +++ b/android/ReactCommon/react/renderer/graphics/platform/android/react/renderer/graphics/PlatformColorParser.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +inline ColorComponents parsePlatformColor( + const PropsParserContext &context, + const RawValue &value) { + ColorComponents colorComponents = {0, 0, 0, 0}; + + if (value.hasType>>()) { + auto map = (better::map>)value; + auto resourcePaths = map["resource_paths"]; + auto dynamicResourcePaths = folly::dynamic::array(); + for (const auto &resourcePath : resourcePaths) { + dynamicResourcePaths.push_back(resourcePath); + } + folly::dynamic dynamicPlatformColor = folly::dynamic::object(); + dynamicPlatformColor["resource_paths"] = dynamicResourcePaths; + + auto fabricUIManager = + context.contextContainer.at>( + "FabricUIManager"); + + static auto getColorFromJava = + facebook::jni::findClassStatic( + "com/facebook/react/fabric/FabricUIManager") + ->getMethod("getColor"); + + jni::local_ref dynamicPlatformColorRNM = + ReadableNativeMap::newObjectCxxArgs(dynamicPlatformColor); + jni::local_ref dynamicPlatformColorRM = + jni::make_local(reinterpret_cast( + dynamicPlatformColorRNM.get())); + + auto color = getColorFromJava( + fabricUIManager, context.surfaceId, dynamicPlatformColorRM.get()); + + dynamicPlatformColorRM.reset(); + dynamicPlatformColorRNM.reset(); + + auto argb = (int64_t)color; + auto ratio = 255.f; + colorComponents.alpha = ((argb >> 24) & 0xFF) / ratio; + colorComponents.red = ((argb >> 16) & 0xFF) / ratio; + colorComponents.green = ((argb >> 8) & 0xFF) / ratio; + colorComponents.blue = (argb & 0xFF) / ratio; + } + + return colorComponents; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.cpp b/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.cpp index c54b57cc503fc..455ce60dc5371 100644 --- a/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.cpp +++ b/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.cpp @@ -10,22 +10,31 @@ namespace facebook { namespace react { +bool isColorMeaningful(SharedColor const &color) noexcept { + if (!color) { + return false; + } + + return colorComponentsFromColor(color).alpha > 0; +} + SharedColor colorFromComponents(ColorComponents components) { - float ratio = 255.9999; + float ratio = 255; return SharedColor( - ((int)(components.alpha * ratio) & 0xff) << 24 | - ((int)(components.red * ratio) & 0xff) << 16 | - ((int)(components.green * ratio) & 0xff) << 8 | - ((int)(components.blue * ratio) & 0xff)); + ((int)round(components.alpha * ratio) & 0xff) << 24 | + ((int)round(components.red * ratio) & 0xff) << 16 | + ((int)round(components.green * ratio) & 0xff) << 8 | + ((int)round(components.blue * ratio) & 0xff)); } ColorComponents colorComponentsFromColor(SharedColor sharedColor) { - float ratio = 256; + float ratio = 255; Color color = *sharedColor; - return ColorComponents{(float)((color >> 16) & 0xff) / ratio, - (float)((color >> 8) & 0xff) / ratio, - (float)((color >> 0) & 0xff) / ratio, - (float)((color >> 24) & 0xff) / ratio}; + return ColorComponents{ + (float)((color >> 16) & 0xff) / ratio, + (float)((color >> 8) & 0xff) / ratio, + (float)((color >> 0) & 0xff) / ratio, + (float)((color >> 24) & 0xff) / ratio}; } SharedColor clearColor() { diff --git a/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.h b/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.h index 86196c6a40487..34c7f6f53f3eb 100644 --- a/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.h +++ b/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -58,6 +59,7 @@ class SharedColor { Color color_; }; +bool isColorMeaningful(SharedColor const &color) noexcept; SharedColor colorFromComponents(ColorComponents components); ColorComponents colorComponentsFromColor(SharedColor color); diff --git a/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/PlatformColorParser.h b/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/PlatformColorParser.h new file mode 100644 index 0000000000000..c48403ba22ab7 --- /dev/null +++ b/android/ReactCommon/react/renderer/graphics/platform/cxx/react/renderer/graphics/PlatformColorParser.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +inline ColorComponents parsePlatformColor( + const PropsParserContext &context, + const RawValue &value) { + float alpha = 0; + float red = 0; + float green = 0; + float blue = 0; + + return {red, green, blue, alpha}; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp b/android/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp index c1023384cac7b..0c42253b94e9d 100644 --- a/android/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp +++ b/android/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp @@ -11,13 +11,21 @@ namespace facebook { namespace react { +bool isColorMeaningful(SharedColor const &color) noexcept { + if (!color) { + return false; + } + + return colorComponentsFromColor(color).alpha > 0; +} + SharedColor colorFromComponents(ColorComponents components) { - float ratio = 255.9999; + float ratio = 255; return SharedColor( - ((int)(components.alpha * ratio) & 0xff) << 24 | - ((int)(components.red * ratio) & 0xff) << 16 | - ((int)(components.green * ratio) & 0xff) << 8 | - ((int)(components.blue * ratio) & 0xff)); + ((int)round(components.alpha * ratio) & 0xff) << 24 | + ((int)round(components.red * ratio) & 0xff) << 16 | + ((int)round(components.green * ratio) & 0xff) << 8 | + ((int)round(components.blue * ratio) & 0xff)); } ColorComponents colorComponentsFromColor(SharedColor sharedColor) { @@ -27,12 +35,13 @@ ColorComponents colorComponentsFromColor(SharedColor sharedColor) { return ColorComponents{0, 0, 0, 0}; } - float ratio = 256; + float ratio = 255; Color color = *sharedColor; - return ColorComponents{(float)((color >> 16) & 0xff) / ratio, - (float)((color >> 8) & 0xff) / ratio, - (float)((color >> 0) & 0xff) / ratio, - (float)((color >> 24) & 0xff) / ratio}; + return ColorComponents{ + (float)((color >> 16) & 0xff) / ratio, + (float)((color >> 8) & 0xff) / ratio, + (float)((color >> 0) & 0xff) / ratio, + (float)((color >> 24) & 0xff) / ratio}; } SharedColor clearColor() { diff --git a/android/ReactCommon/react/renderer/graphics/platform/ios/Color.h b/android/ReactCommon/react/renderer/graphics/platform/ios/Color.h index 0b8ab406b40f5..5078b43f1e4b7 100644 --- a/android/ReactCommon/react/renderer/graphics/platform/ios/Color.h +++ b/android/ReactCommon/react/renderer/graphics/platform/ios/Color.h @@ -8,7 +8,9 @@ #pragma once #include +#include +#include #include #include @@ -17,8 +19,42 @@ namespace react { using Color = int32_t; -using SharedColor = better::optional; +class SharedColor { + public: + static const Color UndefinedColor = std::numeric_limits::max(); + SharedColor() : color_(UndefinedColor) {} + + SharedColor(const SharedColor &sharedColor) : color_(sharedColor.color_) {} + + SharedColor(Color color) : color_(color) {} + + SharedColor &operator=(const SharedColor &sharedColor) { + color_ = sharedColor.color_; + return *this; + } + + Color operator*() const { + return color_; + } + + bool operator==(const SharedColor &otherColor) const { + return color_ == otherColor.color_; + } + + bool operator!=(const SharedColor &otherColor) const { + return color_ != otherColor.color_; + } + + operator bool() const { + return color_ != UndefinedColor; + } + + private: + Color color_; +}; + +bool isColorMeaningful(SharedColor const &color) noexcept; SharedColor colorFromComponents(ColorComponents components); ColorComponents colorComponentsFromColor(SharedColor color); @@ -28,3 +64,10 @@ SharedColor whiteColor(); } // namespace react } // namespace facebook + +template <> +struct std::hash { + std::size_t operator()(facebook::react::SharedColor const &color) const { + return hash()(*color); + } +}; diff --git a/android/ReactCommon/react/renderer/graphics/platform/ios/PlatformColorParser.h b/android/ReactCommon/react/renderer/graphics/platform/ios/PlatformColorParser.h new file mode 100644 index 0000000000000..3437b280760d9 --- /dev/null +++ b/android/ReactCommon/react/renderer/graphics/platform/ios/PlatformColorParser.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +inline ColorComponents parsePlatformColor( + const PropsParserContext &context, + const RawValue &value) { + if (value.hasType>()) { + auto items = (better::map)value; + if (items.find("semantic") != items.end() && + items.at("semantic").hasType>()) { + auto semanticItems = (std::vector)items.at("semantic"); + return RCTPlatformColorComponentsFromSemanticItems(semanticItems); + } + } + + return {0, 0, 0, 0}; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.h b/android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.h new file mode 100644 index 0000000000000..6994c1e0b856e --- /dev/null +++ b/android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems( + std::vector &semanticItems); diff --git a/android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.mm b/android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.mm new file mode 100644 index 0000000000000..baa232423a85b --- /dev/null +++ b/android/ReactCommon/react/renderer/graphics/platform/ios/RCTPlatformColorUtils.mm @@ -0,0 +1,206 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTPlatformColorUtils.h" + +#import +#import +#import + +#include + +NS_ASSUME_NONNULL_BEGIN + +static NSString *const kColorSuffix = @"Color"; +static NSString *const kFallbackARGBKey = @"fallback-argb"; + +static NSDictionary *_PlatformColorSelectorsDict() +{ + static NSDictionary *dict; + static dispatch_once_t once_token; + dispatch_once(&once_token, ^(void) { + dict = @{ + // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors + // Label Colors + @"label" : @{ + kFallbackARGBKey : @(0xFF000000) // iOS 13.0 + }, + @"secondaryLabel" : @{ + kFallbackARGBKey : @(0x993c3c43) // iOS 13.0 + }, + @"tertiaryLabel" : @{ + kFallbackARGBKey : @(0x4c3c3c43) // iOS 13.0 + }, + @"quaternaryLabel" : @{ + kFallbackARGBKey : @(0x2d3c3c43) // iOS 13.0 + }, + // Fill Colors + @"systemFill" : @{ + kFallbackARGBKey : @(0x33787880) // iOS 13.0 + }, + @"secondarySystemFill" : @{ + kFallbackARGBKey : @(0x28787880) // iOS 13.0 + }, + @"tertiarySystemFill" : @{ + kFallbackARGBKey : @(0x1e767680) // iOS 13.0 + }, + @"quaternarySystemFill" : @{ + kFallbackARGBKey : @(0x14747480) // iOS 13.0 + }, + // Text Colors + @"placeholderText" : @{ + kFallbackARGBKey : @(0x4c3c3c43) // iOS 13.0 + }, + // Standard Content Background Colors + @"systemBackground" : @{ + kFallbackARGBKey : @(0xFFffffff) // iOS 13.0 + }, + @"secondarySystemBackground" : @{ + kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0 + }, + @"tertiarySystemBackground" : @{ + kFallbackARGBKey : @(0xFFffffff) // iOS 13.0 + }, + // Grouped Content Background Colors + @"systemGroupedBackground" : @{ + kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0 + }, + @"secondarySystemGroupedBackground" : @{ + kFallbackARGBKey : @(0xFFffffff) // iOS 13.0 + }, + @"tertiarySystemGroupedBackground" : @{ + kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0 + }, + // Separator Colors + @"separator" : @{ + kFallbackARGBKey : @(0x493c3c43) // iOS 13.0 + }, + @"opaqueSeparator" : @{ + kFallbackARGBKey : @(0xFFc6c6c8) // iOS 13.0 + }, + // Link Color + @"link" : @{ + kFallbackARGBKey : @(0xFF007aff) // iOS 13.0 + }, + // Nonadaptable Colors + @"darkText" : @{}, + @"lightText" : @{}, + // https://developer.apple.com/documentation/uikit/uicolor/standard_colors + // Adaptable Colors + @"systemBlue" : @{}, + @"systemBrown" : @{ + kFallbackARGBKey : @(0xFFa2845e) // iOS 13.0 + }, + @"systemGreen" : @{}, + @"systemIndigo" : @{ + kFallbackARGBKey : @(0xFF5856d6) // iOS 13.0 + }, + @"systemOrange" : @{}, + @"systemPink" : @{}, + @"systemPurple" : @{}, + @"systemRed" : @{}, + @"systemTeal" : @{}, + @"systemYellow" : @{}, + // Adaptable Gray Colors + @"systemGray" : @{}, + @"systemGray2" : @{ + kFallbackARGBKey : @(0xFFaeaeb2) // iOS 13.0 + }, + @"systemGray3" : @{ + kFallbackARGBKey : @(0xFFc7c7cc) // iOS 13.0 + }, + @"systemGray4" : @{ + kFallbackARGBKey : @(0xFFd1d1d6) // iOS 13.0 + }, + @"systemGray5" : @{ + kFallbackARGBKey : @(0xFFe5e5ea) // iOS 13.0 + }, + @"systemGray6" : @{ + kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0 + }, + // Transparent Color + @"clear" : @{ + kFallbackARGBKey : @(0x00000000) // iOS 13.0 + }, + }; + }); + return dict; +} + +static UIColor *_UIColorFromHexValue(NSNumber *hexValue) +{ + NSUInteger hexIntValue = [hexValue unsignedIntegerValue]; + + CGFloat red = ((CGFloat)((hexIntValue & 0xFF000000) >> 24)) / 255.0; + CGFloat green = ((CGFloat)((hexIntValue & 0xFF0000) >> 16)) / 255.0; + CGFloat blue = ((CGFloat)((hexIntValue & 0xFF00) >> 8)) / 255.0; + CGFloat alpha = ((CGFloat)(hexIntValue & 0xFF)) / 255.0; + + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; +} + +static UIColor *_Nullable _UIColorFromSemanticString(NSString *semanticString) +{ + NSString *platformColorString = [semanticString hasSuffix:kColorSuffix] + ? [semanticString substringToIndex:[semanticString length] - [kColorSuffix length]] + : semanticString; + NSDictionary *platformColorSelectorsDict = _PlatformColorSelectorsDict(); + NSDictionary *colorInfo = platformColorSelectorsDict[platformColorString]; + if (colorInfo) { + SEL objcColorSelector = NSSelectorFromString([platformColorString stringByAppendingString:kColorSuffix]); + if (![UIColor respondsToSelector:objcColorSelector]) { + NSNumber *fallbackRGB = colorInfo[kFallbackARGBKey]; + if (fallbackRGB) { + return _UIColorFromHexValue(fallbackRGB); + } + } else { + Class uiColorClass = [UIColor class]; + IMP imp = [uiColorClass methodForSelector:objcColorSelector]; + id (*getUIColor)(id, SEL) = ((id(*)(id, SEL))imp); + id colorObject = getUIColor(uiColorClass, objcColorSelector); + if ([colorObject isKindOfClass:[UIColor class]]) { + return colorObject; + } + } + } + return nil; +} + +static inline NSString *_NSStringFromCString( + const std::string &string, + const NSStringEncoding &encoding = NSUTF8StringEncoding) +{ + return [NSString stringWithCString:string.c_str() encoding:encoding]; +} + +static inline facebook::react::ColorComponents _ColorComponentsFromUIColor(UIColor *color) +{ + CGFloat rgba[4]; + RCTGetRGBAColorComponents(color.CGColor, rgba); + return {(float)rgba[0], (float)rgba[1], (float)rgba[2], (float)rgba[3]}; +} + +facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems(std::vector &semanticItems) +{ + for (const auto &semanticCString : semanticItems) { + if (@available(iOS 11.0, *)) { + NSString *semanticNSString = _NSStringFromCString(semanticCString); + UIColor *uiColor = [UIColor colorNamed:semanticNSString]; + if (uiColor != nil) { + return _ColorComponentsFromUIColor(uiColor); + } + uiColor = _UIColorFromSemanticString(semanticNSString); + if (uiColor != nil) { + return _ColorComponentsFromUIColor(uiColor); + } + } + } + + return {0, 0, 0, 0}; +} + +NS_ASSUME_NONNULL_END diff --git a/android/ReactCommon/react/renderer/graphics/rounding.h b/android/ReactCommon/react/renderer/graphics/rounding.h index 8427303fe8a30..b43b9d494d4b2 100644 --- a/android/ReactCommon/react/renderer/graphics/rounding.h +++ b/android/ReactCommon/react/renderer/graphics/rounding.h @@ -30,20 +30,23 @@ Float roundToPixel(Float value, Float scaleFactor) { template Point roundToPixel(Point value, Float scaleFactor) { - return Point{roundToPixel(value.x, scaleFactor), - roundToPixel(value.y, scaleFactor)}; + return Point{ + roundToPixel(value.x, scaleFactor), + roundToPixel(value.y, scaleFactor)}; } template Size roundToPixel(Size value, Float scaleFactor) { - return Size{roundToPixel(value.width, scaleFactor), - roundToPixel(value.height, scaleFactor)}; + return Size{ + roundToPixel(value.width, scaleFactor), + roundToPixel(value.height, scaleFactor)}; } template Rect roundToPixel(Rect value, Float scaleFactor) { - return Rect{roundToPixel(value.origin), - roundToPixel(value.size)}; + return Rect{ + roundToPixel(value.origin), + roundToPixel(value.size)}; } /* diff --git a/android/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp b/android/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp index c11c0c2b70114..9c40fc341f49e 100644 --- a/android/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp +++ b/android/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp @@ -6,6 +6,7 @@ */ #include + #include #include diff --git a/android/ReactCommon/react/renderer/imagemanager/Android.mk b/android/ReactCommon/react/renderer/imagemanager/Android.mk index 0eca0e748917b..83f3ae91f45e5 100644 --- a/android/ReactCommon/react/renderer/imagemanager/Android.mk +++ b/android/ReactCommon/react/renderer/imagemanager/Android.mk @@ -11,7 +11,7 @@ LOCAL_MODULE := react_render_imagemanager LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp $(LOCAL_PATH)/platform/cxx/react/renderer/imagemanager/*.cpp) -LOCAL_SHARED_LIBRARIES := libfolly_json libyoga libfolly_json libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_mounting +LOCAL_SHARED_LIBRARIES := libfolly_json libyoga libfolly_json libreact_render_core libreact_render_debug libreact_render_graphics libreact_render_mounting libreact_debug LOCAL_STATIC_LIBRARIES := @@ -22,7 +22,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/platform/cxx/ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) @@ -32,4 +32,5 @@ $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/mounting) +$(call import-module,react/debug) $(call import-module,yogajni) diff --git a/android/ReactCommon/react/renderer/imagemanager/BUCK b/android/ReactCommon/react/renderer/imagemanager/BUCK index 52f99b3fec6fc..557d5f3c294ce 100644 --- a/android/ReactCommon/react/renderer/imagemanager/BUCK +++ b/android/ReactCommon/react/renderer/imagemanager/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -31,12 +31,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/imagemanager", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], contacts = ["oncall+react_native@xmail.facebook.com"], fbandroid_exported_headers = subdir_glob( [ @@ -64,8 +58,8 @@ rn_xplat_cxx_library( ], force_static = True, ios_deps = [ - "//xplat/js:RCTImage", "//xplat/js/react-native-github:RCTCxxBridge", + "//xplat/js/react-native-github:RCTImage", ], ios_exported_headers = subdir_glob( [ @@ -105,6 +99,7 @@ rn_xplat_cxx_library( "//xplat/folly:headers_only", "//xplat/folly:molly", YOGA_CXX_TARGET, + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/debug:debug"), @@ -119,7 +114,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/imagemanager/ImageRequest.h b/android/ReactCommon/react/renderer/imagemanager/ImageRequest.h index 48da2253099a1..2c181d3b9b28f 100644 --- a/android/ReactCommon/react/renderer/imagemanager/ImageRequest.h +++ b/android/ReactCommon/react/renderer/imagemanager/ImageRequest.h @@ -52,9 +52,7 @@ class ImageRequest final { /* * Returns the Image Source associated with the request. */ - const ImageSource getImageSource() const { - return imageSource_; - } + const ImageSource &getImageSource() const; /* * Returns stored observer coordinator as a shared pointer. diff --git a/android/ReactCommon/react/renderer/imagemanager/ImageResponseObserverCoordinator.cpp b/android/ReactCommon/react/renderer/imagemanager/ImageResponseObserverCoordinator.cpp index 3f3fd6a5ffbbb..c75fa5b4c19f7 100644 --- a/android/ReactCommon/react/renderer/imagemanager/ImageResponseObserverCoordinator.cpp +++ b/android/ReactCommon/react/renderer/imagemanager/ImageResponseObserverCoordinator.cpp @@ -8,7 +8,8 @@ #include "ImageResponseObserverCoordinator.h" #include -#include + +#include namespace facebook { namespace react { @@ -52,7 +53,7 @@ void ImageResponseObserverCoordinator::nativeImageResponseProgress( float progress) const { mutex_.lock(); auto observers = observers_; - assert(status_ == ImageResponse::Status::Loading); + react_native_assert(status_ == ImageResponse::Status::Loading); mutex_.unlock(); for (auto observer : observers) { @@ -65,7 +66,7 @@ void ImageResponseObserverCoordinator::nativeImageResponseComplete( mutex_.lock(); imageData_ = imageResponse.getImage(); imageMetadata_ = imageResponse.getMetadata(); - assert(status_ == ImageResponse::Status::Loading); + react_native_assert(status_ == ImageResponse::Status::Loading); status_ = ImageResponse::Status::Completed; auto observers = observers_; mutex_.unlock(); @@ -77,7 +78,7 @@ void ImageResponseObserverCoordinator::nativeImageResponseComplete( void ImageResponseObserverCoordinator::nativeImageResponseFailed() const { mutex_.lock(); - assert(status_ == ImageResponse::Status::Loading); + react_native_assert(status_ == ImageResponse::Status::Loading); status_ = ImageResponse::Status::Failed; auto observers = observers_; mutex_.unlock(); diff --git a/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.cpp b/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.cpp index 43a6134495ecd..40524e5b0ff0f 100644 --- a/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.cpp +++ b/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.cpp @@ -10,17 +10,11 @@ namespace facebook { namespace react { -void ImageTelemetry::willRequestUrl() { - assert(willRequestUrlTime_ == kTelemetryUndefinedTimePoint); - willRequestUrlTime_ = telemetryTimePointNow(); -} - SurfaceId ImageTelemetry::getSurfaceId() const { return surfaceId_; } TelemetryTimePoint ImageTelemetry::getWillRequestUrlTime() const { - assert(willRequestUrlTime_ != kTelemetryUndefinedTimePoint); return willRequestUrlTime_; } diff --git a/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h b/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h index 3f053dbcf3ea1..1ffaffd0a4aa0 100644 --- a/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h +++ b/android/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h @@ -15,25 +15,20 @@ namespace react { /* * Represents telemetry data associated with a image request + * where the willRequestUrlTime is the time at ImageTelemetry's creation. */ class ImageTelemetry final { public: - ImageTelemetry(SurfaceId const surfaceId) : surfaceId_(surfaceId) {} + ImageTelemetry(SurfaceId const surfaceId) : surfaceId_(surfaceId) { + willRequestUrlTime_ = telemetryTimePointNow(); + } - /* - * Signaling - */ - void willRequestUrl(); - - /* - * Reading - */ TelemetryTimePoint getWillRequestUrlTime() const; SurfaceId getSurfaceId() const; private: - TelemetryTimePoint willRequestUrlTime_{kTelemetryUndefinedTimePoint}; + TelemetryTimePoint willRequestUrlTime_; const SurfaceId surfaceId_; }; diff --git a/android/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp b/android/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp index e9ce8dcb9a02b..ac01f5b1c1903 100644 --- a/android/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp +++ b/android/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp @@ -11,7 +11,7 @@ namespace facebook { namespace react { ImageRequest::ImageRequest( - const ImageSource &imageSource, + ImageSource const &imageSource, std::shared_ptr telemetry) : imageSource_(imageSource), telemetry_(telemetry) { coordinator_ = std::make_shared(); @@ -24,6 +24,7 @@ ImageRequest::ImageRequest(ImageRequest &&other) noexcept other.coordinator_ = nullptr; other.cancelRequest_ = nullptr; other.telemetry_ = nullptr; + other.imageSource_ = {}; } ImageRequest::~ImageRequest() { @@ -37,6 +38,10 @@ void ImageRequest::setCancelationFunction( cancelRequest_ = cancelationFunction; } +const ImageSource &ImageRequest::getImageSource() const { + return imageSource_; +} + const std::shared_ptr &ImageRequest::getSharedTelemetry() const { return telemetry_; diff --git a/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm b/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm index 3fa8c36b29174..4922b96ef03d3 100644 --- a/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm +++ b/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm @@ -44,7 +44,6 @@ - (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfa std::shared_ptr telemetry; if ([self->_imageLoader shouldEnablePerfLoggingForRequestUrl:request.URL]) { telemetry = std::make_shared(surfaceId); - telemetry->willRequestUrl(); } else { telemetry = nullptr; } diff --git a/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImagePrimitivesConversions.h b/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImagePrimitivesConversions.h index f769d4d686abe..ec37101afa1b0 100644 --- a/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImagePrimitivesConversions.h +++ b/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImagePrimitivesConversions.h @@ -12,19 +12,21 @@ using namespace facebook::react; -inline static RCTResizeMode RCTResizeModeFromImageResizeMode(ImageResizeMode imageResizeMode) +inline static UIViewContentMode RCTContentModeFromImageResizeMode(ImageResizeMode imageResizeMode) { switch (imageResizeMode) { case ImageResizeMode::Cover: - return RCTResizeModeCover; + return UIViewContentModeScaleAspectFill; case ImageResizeMode::Contain: - return RCTResizeModeContain; + return UIViewContentModeScaleAspectFit; case ImageResizeMode::Stretch: - return RCTResizeModeStretch; + return UIViewContentModeScaleToFill; case ImageResizeMode::Center: - return RCTResizeModeCenter; + return UIViewContentModeCenter; case ImageResizeMode::Repeat: - return RCTResizeModeRepeat; + // Repeat resize mode is handled by the UIImage. Use scale to fill + // so the repeated image fills the UIImageView. + return UIViewContentModeScaleToFill; } } diff --git a/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm b/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm index 7a36d42ea4a28..19aeafdc30094 100644 --- a/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm +++ b/android/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm @@ -92,7 +92,7 @@ - (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfa auto result = dispatch_group_wait(imageWaitGroup, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC)); if (result != 0) { - RCTLogError(@"Getting an image timed out"); + RCTLogError(@"Image timed out in test environment for url: %@", loaderRequest.imageURL); } return imageRequest; } diff --git a/android/ReactCommon/react/renderer/leakchecker/Android.mk b/android/ReactCommon/react/renderer/leakchecker/Android.mk new file mode 100644 index 0000000000000..48056bc8dd260 --- /dev/null +++ b/android/ReactCommon/react/renderer/leakchecker/Android.mk @@ -0,0 +1,29 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_render_leakchecker + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_SHARED_LIBRARIES := libruntimeexecutor libreact_render_core glog + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"Fabric\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,react/renderer/core) +$(call import-module,glog) +$(call import-module,runtimeexecutor) diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/BUCK b/android/ReactCommon/react/renderer/leakchecker/BUCK similarity index 58% rename from android/ReactCommon/react/renderer/components/picker/iospicker/BUCK rename to android/ReactCommon/react/renderer/leakchecker/BUCK index 8e48198de3ea7..c0796f177a32c 100644 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/BUCK +++ b/android/ReactCommon/react/renderer/leakchecker/BUCK @@ -1,12 +1,11 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "CXX", - "YOGA_CXX_TARGET", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -15,30 +14,27 @@ load( APPLE_COMPILER_FLAGS = get_apple_compiler_flags() rn_xplat_cxx_library( - name = "iospicker", + name = "leakchecker", srcs = glob( ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), ), headers = glob( ["**/*.h"], + exclude = glob(["tests/**/*.h"]), ), header_namespace = "", exported_headers = subdir_glob( [ ("", "*.h"), ], - prefix = "react/renderer/components/iospicker", + prefix = "react/renderer/leakchecker", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", @@ -46,13 +42,7 @@ rn_xplat_cxx_library( ], visibility = ["PUBLIC"], deps = [ - "//xplat/folly:headers_only", - YOGA_CXX_TARGET, - react_native_xplat_target("react/utils:utils"), - react_native_xplat_target("react/renderer/attributedstring:attributedstring"), react_native_xplat_target("react/renderer/core:core"), - react_native_xplat_target("react/renderer/graphics:graphics"), - react_native_xplat_target("react/renderer/components/text:text"), - react_native_xplat_target("react/renderer/components/view:view"), + react_native_xplat_target("runtimeexecutor:runtimeexecutor"), ], ) diff --git a/android/ReactCommon/react/renderer/leakchecker/LeakChecker.cpp b/android/ReactCommon/react/renderer/leakchecker/LeakChecker.cpp new file mode 100644 index 0000000000000..a72e485665f2e --- /dev/null +++ b/android/ReactCommon/react/renderer/leakchecker/LeakChecker.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "LeakChecker.h" + +#include +#include + +namespace facebook { +namespace react { + +LeakChecker::LeakChecker(RuntimeExecutor const &runtimeExecutor) + : runtimeExecutor_(runtimeExecutor) {} + +void LeakChecker::uiManagerDidCreateShadowNodeFamily( + ShadowNodeFamily::Shared const &shadowNodeFamily) const { + registry_.add(shadowNodeFamily); +} + +void LeakChecker::stopSurface(SurfaceId surfaceId) { + if (previouslyStoppedSurface_ > 0) { + // Dispatch the check onto JavaScript thread to make sure all other + // cleanup code has had chance to run. + runtimeExecutor_([previouslySoppedSurface = previouslyStoppedSurface_, + this](jsi::Runtime &runtime) { + runtime.instrumentation().collectGarbage("LeakChecker"); + // For now check the previous surface because React uses double + // buffering which keeps the surface that was just stopped in + // memory. This is a documented problem in the last point of + // https://github.com/facebook/react/issues/16087 + checkSurfaceForLeaks(previouslySoppedSurface); + }); + } + + previouslyStoppedSurface_ = surfaceId; +} + +void LeakChecker::checkSurfaceForLeaks(SurfaceId surfaceId) const { + auto weakFamilies = registry_.weakFamiliesForSurfaceId(surfaceId); + unsigned int numberOfLeaks = 0; + for (auto const &weakFamily : weakFamilies) { + auto strong = weakFamily.lock(); + if (strong) { + ++numberOfLeaks; + } + } + if (numberOfLeaks > 0) { + LOG(ERROR) << "[LeakChecker] Surface with id: " << surfaceId + << " has leaked " << numberOfLeaks << " components out of " + << weakFamilies.size(); + } + registry_.removeFamiliesWithSurfaceId(surfaceId); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/leakchecker/LeakChecker.h b/android/ReactCommon/react/renderer/leakchecker/LeakChecker.h new file mode 100644 index 0000000000000..a9bb3dd004096 --- /dev/null +++ b/android/ReactCommon/react/renderer/leakchecker/LeakChecker.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +using GarbageCollectionTrigger = std::function; + +class LeakChecker final { + public: + LeakChecker(RuntimeExecutor const &runtimeExecutor); + + void uiManagerDidCreateShadowNodeFamily( + ShadowNodeFamily::Shared const &shadowNodeFamily) const; + void stopSurface(SurfaceId surfaceId); + + private: + void checkSurfaceForLeaks(SurfaceId surfaceId) const; + + RuntimeExecutor const runtimeExecutor_{}; + + WeakFamilyRegistry registry_{}; + SurfaceId previouslyStoppedSurface_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.cpp b/android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.cpp new file mode 100644 index 0000000000000..ffb2e161d485f --- /dev/null +++ b/android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "WeakFamilyRegistry.h" + +namespace facebook { +namespace react { + +void WeakFamilyRegistry::add( + ShadowNodeFamily::Shared const &shadowNodeFamily) const { + std::lock_guard lock(familiesMutex_); + ShadowNodeFamily::Weak weakFamily = shadowNodeFamily; + families_[shadowNodeFamily->getSurfaceId()].push_back(weakFamily); +} + +void WeakFamilyRegistry::removeFamiliesWithSurfaceId( + SurfaceId surfaceId) const { + std::lock_guard lock(familiesMutex_); + families_.erase(surfaceId); +} + +WeakFamilyRegistry::WeakFamilies WeakFamilyRegistry::weakFamiliesForSurfaceId( + SurfaceId surfaceId) const { + std::lock_guard lock(familiesMutex_); + if (families_.find(surfaceId) != families_.end()) { + return families_[surfaceId]; + } else { + return {}; + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.h b/android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.h new file mode 100644 index 0000000000000..702eb05152ec7 --- /dev/null +++ b/android/ReactCommon/react/renderer/leakchecker/WeakFamilyRegistry.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class WeakFamilyRegistry final { + public: + using WeakFamilies = std::vector; + + void add(ShadowNodeFamily::Shared const &shadowNodeFamily) const; + void removeFamiliesWithSurfaceId(SurfaceId surfaceId) const; + WeakFamilies weakFamiliesForSurfaceId(SurfaceId surfaceId) const; + + private: + /** + * Mutex protecting `families_` property. + */ + mutable std::mutex familiesMutex_; + + /** + * A map of ShadowNodeFamily used on surface. + */ + mutable std::unordered_map families_{}; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mapbuffer/Android.mk b/android/ReactCommon/react/renderer/mapbuffer/Android.mk index f496322b1dde9..57c51b2ec4c91 100644 --- a/android/ReactCommon/react/renderer/mapbuffer/Android.mk +++ b/android/ReactCommon/react/renderer/mapbuffer/Android.mk @@ -9,19 +9,22 @@ include $(CLEAR_VARS) LOCAL_MODULE := react_render_mapbuffer -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ -LOCAL_SHARED_LIBRARIES := libreact_utils - LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +LOCAL_STATIC_LIBRARIES := + +LOCAL_SHARED_LIBRARIES := glog libglog_init libreact_debug include $(BUILD_SHARED_LIBRARY) -$(call import-module,react/utils) +$(call import-module,glog) +$(call import-module,fbgloginit) +$(call import-module,react/debug) diff --git a/android/ReactCommon/react/renderer/mapbuffer/BUCK b/android/ReactCommon/react/renderer/mapbuffer/BUCK index 5bd4038921efe..8e57a23c7575f 100644 --- a/android/ReactCommon/react/renderer/mapbuffer/BUCK +++ b/android/ReactCommon/react/renderer/mapbuffer/BUCK @@ -2,11 +2,16 @@ load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "fb_xplat_cxx_test", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", ) +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + rn_xplat_cxx_library( name = "mapbuffer", srcs = glob( @@ -22,20 +27,17 @@ rn_xplat_cxx_library( [ ("", "*.h"), ], - prefix = "react", + prefix = "react/renderer/mapbuffer", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], - platforms = (ANDROID), + platforms = ANDROID, preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", ], tests = [":tests"], visibility = ["PUBLIC"], @@ -44,7 +46,7 @@ rn_xplat_cxx_library( "//xplat/fbsystrace:fbsystrace", "//xplat/folly:headers_only", "//xplat/folly:memory", - react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/debug:debug"), ], ) @@ -55,13 +57,15 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], - platforms = (ANDROID), + platforms = ANDROID, deps = [ "//xplat/folly:molly", "//xplat/third-party/gmock:gtest", + react_native_xplat_target("react/debug:debug"), + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], ) diff --git a/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp b/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp index 085ccae66c975..80e76dfb4f2e0 100644 --- a/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp +++ b/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp @@ -7,12 +7,143 @@ #include "MapBuffer.h" +using namespace facebook::react; + namespace facebook { namespace react { -MapBuffer::MapBuffer() {} +// TODO T83483191: Extend MapBuffer C++ implementation to support basic random +// access +MapBuffer::MapBuffer(uint8_t *const data, int32_t dataSize) { + react_native_assert( + (data != nullptr) && "Error trying to build an invalid MapBuffer"); + + // Should we move the memory here or document it? + data_ = data; + + count_ = 0; + memcpy( + reinterpret_cast(&count_), + reinterpret_cast(data_ + HEADER_COUNT_OFFSET), + UINT16_SIZE); + + // TODO T83483191: extract memcpy calls into an inline function to simplify + // the code + dataSize_ = 0; + memcpy( + reinterpret_cast(&dataSize_), + reinterpret_cast(data_ + HEADER_BUFFER_SIZE_OFFSET), + INT_SIZE); + + if (dataSize != dataSize_) { + LOG(ERROR) << "Error: Data size does not match, expected " << dataSize + << " found: " << dataSize_; + abort(); + } +} + +int32_t MapBuffer::getInt(Key key) const { + int32_t value = 0; + memcpy( + reinterpret_cast(&value), + reinterpret_cast(data_ + getValueOffset(key)), + INT_SIZE); + return value; +} + +bool MapBuffer::getBool(Key key) const { + return getInt(key) != 0; +} + +double MapBuffer::getDouble(Key key) const { + // TODO T83483191: extract this code into a "template method" and reuse it for + // other types + double value = 0; + memcpy( + reinterpret_cast(&value), + reinterpret_cast(data_ + getValueOffset(key)), + DOUBLE_SIZE); + return value; +} + +int32_t MapBuffer::getDynamicDataOffset() const { + // The begininig of dynamic data can be calculated as the offset of the next + // key in the map + return getKeyOffset(count_); +} + +std::string MapBuffer::getString(Key key) const { + // TODO T83483191:Add checks to verify that offsets are under the boundaries + // of the map buffer + int32_t dynamicDataOffset = getDynamicDataOffset(); + int32_t stringLength = 0; + int32_t offset = getInt(key); + memcpy( + reinterpret_cast(&stringLength), + reinterpret_cast(data_ + dynamicDataOffset + offset), + INT_SIZE); + + char *value = new char[stringLength]; + + memcpy( + reinterpret_cast(value), + reinterpret_cast( + data_ + dynamicDataOffset + offset + INT_SIZE), + stringLength); + + return std::string(value, 0, stringLength); +} + +MapBuffer MapBuffer::getMapBuffer(Key key) const { + // TODO T83483191: Add checks to verify that offsets are under the boundaries + // of the map buffer + int32_t dynamicDataOffset = getDynamicDataOffset(); + + int32_t mapBufferLength = 0; + int32_t offset = getInt(key); + memcpy( + reinterpret_cast(&mapBufferLength), + reinterpret_cast(data_ + dynamicDataOffset + offset), + INT_SIZE); + + uint8_t *value = new Byte[mapBufferLength]; + + memcpy( + reinterpret_cast(value), + reinterpret_cast( + data_ + dynamicDataOffset + offset + INT_SIZE), + mapBufferLength); + + return MapBuffer(value, mapBufferLength); +} + +bool MapBuffer::isNull(Key key) const { + return getInt(key) == NULL_VALUE; +} + +int32_t MapBuffer::getBufferSize() const { + return dataSize_; +} + +void MapBuffer::copy(uint8_t *output) const { + memcpy(output, data_, dataSize_); +} + +uint16_t MapBuffer::getCount() const { + uint16_t size = 0; + + memcpy( + reinterpret_cast(&size), + reinterpret_cast(data_ + HEADER_COUNT_OFFSET), + + UINT16_SIZE); + + return size; +} -MapBuffer::~MapBuffer() {} +MapBuffer::~MapBuffer() { + delete[] data_; +} } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.h b/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.h index 3ce365990d525..b3f0c3051f176 100644 --- a/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.h +++ b/android/ReactCommon/react/renderer/mapbuffer/MapBuffer.h @@ -7,7 +7,11 @@ #pragma once -#include +#include +#include + +#include +#include namespace facebook { namespace react { @@ -28,9 +32,43 @@ namespace react { * - have minimal APK size and build time impact. */ class MapBuffer { + private: + // Buffer and its size + const uint8_t *data_ = nullptr; + + // amount of bytes in the MapBuffer + int32_t dataSize_ = 0; + + // amount of items in the MapBuffer + uint16_t count_ = 0; + + // returns the relative offset of the first byte of dynamic data + int32_t getDynamicDataOffset() const; + public: - MapBuffer(); - virtual ~MapBuffer(); + MapBuffer(uint8_t *const data, int32_t dataSize); + + ~MapBuffer(); + + int32_t getInt(Key key) const; + + bool getBool(Key key) const; + + double getDouble(Key key) const; + + std::string getString(Key key) const; + + // TODO T83483191: review this declaration + MapBuffer getMapBuffer(Key key) const; + + int32_t getBufferSize() const; + + // TODO T83483191: review parameters of copy method + void copy(uint8_t *output) const; + + bool isNull(Key key) const; + + uint16_t getCount() const; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp b/android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp new file mode 100644 index 0000000000000..4ff2f7b5c5880 --- /dev/null +++ b/android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "MapBufferBuilder.h" + +using namespace facebook::react; + +namespace facebook { +namespace react { + +// TODO T83483191: Add asserts to check overflowing on additions +MapBufferBuilder::MapBufferBuilder() + : MapBufferBuilder::MapBufferBuilder(INITIAL_KEY_VALUE_SIZE) {} + +MapBuffer MapBufferBuilder::EMPTY() { + static auto emptyMap = MapBufferBuilder().build(); + return emptyMap; +} + +MapBufferBuilder::MapBufferBuilder(uint16_t initialSize) { + keyValuesSize_ = initialSize; + keyValues_ = new Byte[keyValuesSize_]; + // First Key should be written right after the header. + keyValuesOffset_ = HEADER_SIZE; + + dynamicDataSize_ = 0; + dynamicDataValues_ = nullptr; + dynamicDataOffset_ = 0; +} + +void MapBufferBuilder::ensureKeyValueSpace() { + int32_t oldKeyValuesSize = keyValuesSize_; + react_native_assert( + (keyValuesSize_ < std::numeric_limits::max() / 2) && + "Error trying to assign a value beyond the capacity of uint16_t: "); + keyValuesSize_ *= 2; + uint8_t *newKeyValues = new Byte[keyValuesSize_]; + uint8_t *oldKeyValues = keyValues_; + memcpy(newKeyValues, keyValues_, oldKeyValuesSize); + keyValues_ = newKeyValues; + delete[] oldKeyValues; +} + +void MapBufferBuilder::storeKeyValue( + Key key, + uint8_t *value, + int32_t valueSize) { + if (key < minKeyToStore_) { + LOG(ERROR) << "Error: key out of order - key: " << key; + abort(); + } + if (valueSize > MAX_VALUE_SIZE) { + LOG(ERROR) << "Error: size of value must be <= MAX_VALUE_SIZE. ValueSize: " + << valueSize; + abort(); + } + + // TODO T83483191: header.count points to the next index + // TODO T83483191: add test to verify storage of sparse keys + int32_t keyOffset = getKeyOffset(_header.count); + int32_t valueOffset = keyOffset + KEY_SIZE; + + int32_t nextKeyValueOffset = keyOffset + BUCKET_SIZE; + if (nextKeyValueOffset >= keyValuesSize_) { + ensureKeyValueSpace(); + } + + memcpy(keyValues_ + keyOffset, &key, KEY_SIZE); + memcpy(keyValues_ + valueOffset, value, valueSize); + + _header.count++; + + minKeyToStore_ = key + 1; + // Move keyValuesOffset_ to the next available [key, value] position + keyValuesOffset_ = std::max(nextKeyValueOffset, keyValuesOffset_); +} + +void MapBufferBuilder::putBool(Key key, bool value) { + putInt(key, (int)value); +} + +void MapBufferBuilder::putDouble(Key key, double value) { + uint8_t *bytePointer = reinterpret_cast(&value); + storeKeyValue(key, bytePointer, DOUBLE_SIZE); +} + +void MapBufferBuilder::putNull(Key key) { + putInt(key, NULL_VALUE); +} + +void MapBufferBuilder::putInt(Key key, int32_t value) { + uint8_t *bytePointer = reinterpret_cast(&(value)); + storeKeyValue(key, bytePointer, INT_SIZE); +} + +void MapBufferBuilder::ensureDynamicDataSpace(int32_t size) { + if (dynamicDataValues_ == nullptr) { + dynamicDataSize_ = size; + dynamicDataValues_ = new Byte[dynamicDataSize_]; + dynamicDataOffset_ = 0; + return; + } + + if (dynamicDataOffset_ + size >= dynamicDataSize_) { + int32_t oldDynamicDataSize = dynamicDataSize_; + react_native_assert( + (dynamicDataSize_ < std::numeric_limits::max() / 2) && + "Error: trying to assign a value beyond the capacity of int"); + dynamicDataSize_ *= dynamicDataSize_; + + react_native_assert( + (dynamicDataSize_ < std::numeric_limits::max() - size) && + "Error: trying to assign a value beyond the capacity of int"); + + // sum size to ensure that the size always fit into newDynamicDataValues + dynamicDataSize_ += size; + uint8_t *newDynamicDataValues = new Byte[dynamicDataSize_]; + uint8_t *oldDynamicDataValues = dynamicDataValues_; + memcpy(newDynamicDataValues, dynamicDataValues_, oldDynamicDataSize); + dynamicDataValues_ = newDynamicDataValues; + delete[] oldDynamicDataValues; + } +} + +void MapBufferBuilder::putString(Key key, std::string value) { + int32_t strLength = static_cast(value.length()); + const char *cstring = getCstring(&value); + + // format [lenght of string (int)] + [Array of Characters in the string] + int32_t sizeOfLength = INT_SIZE; + // TODO T83483191: review if map.getBufferSize() should be an int32_t or long + // instead of an int16 (because strings can be longer than int16); + + int32_t sizeOfDynamicData = sizeOfLength + strLength; + ensureDynamicDataSpace(sizeOfDynamicData); + memcpy(dynamicDataValues_ + dynamicDataOffset_, &strLength, sizeOfLength); + memcpy( + dynamicDataValues_ + dynamicDataOffset_ + sizeOfLength, + cstring, + strLength); + + // Store Key and pointer to the string + putInt(key, dynamicDataOffset_); + + dynamicDataOffset_ += sizeOfDynamicData; +} + +void MapBufferBuilder::putMapBuffer(Key key, MapBuffer &map) { + int32_t mapBufferSize = map.getBufferSize(); + + // format [lenght of buffer (int)] + [bytes of MapBuffer] + int32_t sizeOfDynamicData = mapBufferSize + INT_SIZE; + + // format [Array of bytes of the mapBuffer] + ensureDynamicDataSpace(sizeOfDynamicData); + + memcpy(dynamicDataValues_ + dynamicDataOffset_, &mapBufferSize, INT_SIZE); + // Copy the content of the map into dynamicDataValues_ + map.copy(dynamicDataValues_ + dynamicDataOffset_ + INT_SIZE); + + // Store Key and pointer to the string + putInt(key, dynamicDataOffset_); + + dynamicDataOffset_ += sizeOfDynamicData; +} + +MapBuffer MapBufferBuilder::build() { + react_native_assert( + (keyValues_ != nullptr) && + "Error when building mapbuffer with invalid datastructures."); + + // Create buffer: [header] + [key, values] + [dynamic data] + int32_t bufferSize = keyValuesOffset_ + dynamicDataOffset_; + + _header.bufferSize = bufferSize; + + // Copy header at the beginning of "keyValues_" + memcpy(keyValues_, &_header, HEADER_SIZE); + + uint8_t *buffer = new Byte[bufferSize]; + + memcpy(buffer, keyValues_, keyValuesOffset_); + + if (dynamicDataValues_ != nullptr) { + memcpy(buffer + keyValuesOffset_, dynamicDataValues_, dynamicDataOffset_); + } + + // TODO T83483191: should we use std::move here? + auto map = MapBuffer(buffer, bufferSize); + + // TODO T83483191: we should invalidate the class once the build() method is + // called. + + if (keyValues_ != nullptr) { + delete[] keyValues_; + } + keyValues_ = nullptr; + keyValuesSize_ = 0; + keyValuesOffset_ = 0; + + if (dynamicDataValues_ != nullptr) { + delete[] dynamicDataValues_; + dynamicDataValues_ = nullptr; + } + dynamicDataSize_ = 0; + dynamicDataOffset_ = 0; + _header = {ALIGNMENT, 0, 0}; + + return map; +} + +MapBufferBuilder::~MapBufferBuilder() { + if (keyValues_ != nullptr) { + delete[] keyValues_; + } + if (dynamicDataValues_ != nullptr) { + delete[] dynamicDataValues_; + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h b/android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h new file mode 100644 index 0000000000000..d46dd8fbea0a5 --- /dev/null +++ b/android/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +// Default initial size for _keyValues array +// 108 = 10 entries = 10*10 + 8 sizeof(header) +constexpr uint16_t INITIAL_KEY_VALUE_SIZE = 108; + +// Default initial size for _dynamicDataValues array +constexpr int32_t INITIAL_DYNAMIC_DATA_SIZE = 200; + +/** + * MapBufferBuilder is a builder class for MapBuffer + */ +class MapBufferBuilder { + private: + Header _header = {ALIGNMENT, 0, 0}; + + void ensureKeyValueSpace(); + + void ensureDynamicDataSpace(int32_t size); + + void storeKeyValue(Key key, uint8_t *value, int32_t valueSize); + + // Array of [key,value] map entries: + // - Key is represented in 2 bytes + // - Value is stored into 8 bytes. The 8 bytes of the value will contain the + // actual value for the key or a pointer to the actual value (based on the + // type) + uint8_t *keyValues_ = nullptr; + + // Amount of bytes allocated on _keyValues + uint16_t keyValuesSize_ = 0; + + // Relative offset on the _keyValues array. + // This represents the first byte that can be written in _keyValues array + int32_t keyValuesOffset_ = 0; + + // This array contains data for dynamic values in the MapBuffer. + // A dynamic value is a String or another MapBuffer. + uint8_t *dynamicDataValues_ = nullptr; + + // Amount of bytes allocated on _dynamicDataValues + int32_t dynamicDataSize_ = 0; + + // Relative offset on the _dynamicDataValues array. + // This represents the first byte that can be written in _dynamicDataValues + // array + int32_t dynamicDataOffset_ = 0; + + // Minimmum key to store in the MapBuffer (this is used to guarantee + // consistency) + uint16_t minKeyToStore_ = 0; + + public: + MapBufferBuilder(); + + MapBufferBuilder(uint16_t initialSize); + + ~MapBufferBuilder(); + + static MapBuffer EMPTY(); + + void putInt(Key key, int32_t value); + + void putBool(Key key, bool value); + + void putDouble(Key key, double value); + + void putNull(Key key); + + void putString(Key key, std::string value); + + void putMapBuffer(Key key, MapBuffer &map); + + // TODO T83483191: This should return MapBuffer! + MapBuffer build(); +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mapbuffer/primitives.h b/android/ReactCommon/react/renderer/mapbuffer/primitives.h new file mode 100644 index 0000000000000..456e0b9e12d1d --- /dev/null +++ b/android/ReactCommon/react/renderer/mapbuffer/primitives.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +// TODO T83483191: Enable CHECK_CONSISTENCY only in debug mode or test +// environments (or just in demand) #define CHECK_CONSISTENCY 1 + +constexpr static int32_t NULL_VALUE = 0; + +// Value used to verify if the data is serialized with LittleEndian order +constexpr static uint16_t ALIGNMENT = 0xFE; + +using Key = uint16_t; + +using Byte = uint8_t; + +namespace facebook { +namespace react { + +struct Header { + uint16_t alignment; // alignment of serialization + uint16_t count; // amount of items in the map + int32_t bufferSize; // Amount of bytes used to store the map in memory +}; + +constexpr static int32_t KEY_SIZE = sizeof(Key); +constexpr static int32_t HEADER_SIZE = sizeof(Header); +constexpr static int32_t INT_SIZE = sizeof(int32_t); +constexpr static int32_t DOUBLE_SIZE = sizeof(double); +constexpr static int32_t UINT8_SIZE = sizeof(uint8_t); +constexpr static int32_t UINT16_SIZE = sizeof(uint16_t); +constexpr static int32_t UINT64_SIZE = sizeof(uint64_t); +constexpr static int32_t HEADER_ALIGNMENT_OFFSET = 0; +constexpr static int32_t HEADER_COUNT_OFFSET = UINT16_SIZE; +constexpr static int32_t HEADER_BUFFER_SIZE_OFFSET = UINT16_SIZE * 2; + +constexpr static int32_t MAX_VALUE_SIZE = UINT64_SIZE; + +// 10 bytes : 2 key + 8 value +constexpr static int32_t BUCKET_SIZE = KEY_SIZE + UINT64_SIZE; + +/** + * Returns the offset of the key received by parameter. + */ +inline int32_t getKeyOffset(Key key) { + return HEADER_SIZE + BUCKET_SIZE * key; +} + +/** + * Returns the offset of the value associated to the key received by parameter. + */ +inline int32_t getValueOffset(Key key) { + return getKeyOffset(key) + KEY_SIZE; +} + +static inline const char *getCstring(const std::string *str) { + return str ? str->c_str() : ""; +} + +inline void +checkKeyConsistency(const Header &header, const uint8_t *data, Key key) { +#ifdef CHECK_CONSISTENCY + if (key >= header.count) { + LOG(ERROR) << "Error: Key is higher than size of Map - key '" << key + << "' - size: '" << header.count << "'"; + assert(false && "Error while reading key"); + } + + Key storedKey = 0; + memcpy( + reinterpret_cast(&storedKey), + reinterpret_cast(data + getKeyOffset(key)), + KEY_SIZE); + + if (storedKey != key) { + LOG(ERROR) << "Error while reading key, expecting '" << key << "' found: '" + << storedKey << "'"; + assert(false && "Error while reading key"); + } +#endif +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp b/android/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp index 4785f09ae7e49..9dd36eeb6df99 100644 --- a/android/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp +++ b/android/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp @@ -8,7 +8,149 @@ #include #include +#include +#include -TEST(MapBufferTest, testSomething) { - // TODO +using namespace facebook::react; + +TEST(MapBufferTest, testSimpleIntMap) { + auto builder = MapBufferBuilder(); + + builder.putInt(0, 1234); + builder.putInt(1, 4321); + + auto map = builder.build(); + + EXPECT_EQ(map.getCount(), 2); + EXPECT_EQ(map.getInt(0), 1234); + EXPECT_EQ(map.getInt(1), 4321); +} + +TEST(MapBufferTest, testMapBufferExtension) { + // 26 = 2 buckets: 2*10 + 6 sizeof(header) + int initialSize = 26; + auto buffer = MapBufferBuilder(initialSize); + + buffer.putInt(0, 1234); + buffer.putInt(1, 4321); + buffer.putInt(2, 2121); + buffer.putInt(3, 1212); + + auto map = buffer.build(); + + EXPECT_EQ(map.getCount(), 4); + + EXPECT_EQ(map.getInt(0), 1234); + EXPECT_EQ(map.getInt(1), 4321); + EXPECT_EQ(map.getInt(2), 2121); + EXPECT_EQ(map.getInt(3), 1212); +} + +TEST(MapBufferTest, testBoolEntries) { + auto buffer = MapBufferBuilder(); + + buffer.putBool(0, true); + buffer.putBool(1, false); + + auto map = buffer.build(); + + EXPECT_EQ(map.getCount(), 2); + EXPECT_EQ(map.getBool(0), true); + EXPECT_EQ(map.getBool(1), false); +} + +TEST(MapBufferTest, testNullEntries) { + auto buffer = MapBufferBuilder(); + + buffer.putNull(0); + buffer.putInt(1, 1234); + + auto map = buffer.build(); + + EXPECT_EQ(map.getCount(), 2); + EXPECT_EQ(map.isNull(0), true); + EXPECT_EQ(map.isNull(1), false); + // TODO T83483191: serialize null values to be distinguishable from '0' + // values + // EXPECT_EQ(map.isNull(1), false); + // EXPECT_EQ(map.getBool(1), false); +} + +TEST(MapBufferTest, testDoubleEntries) { + auto buffer = MapBufferBuilder(); + + buffer.putDouble(0, 123.4); + buffer.putDouble(1, 432.1); + + auto map = buffer.build(); + + EXPECT_EQ(map.getCount(), 2); + + EXPECT_EQ(map.getDouble(0), 123.4); + EXPECT_EQ(map.getDouble(1), 432.1); +} + +TEST(MapBufferTest, testStringEntries) { + auto builder = MapBufferBuilder(); + + builder.putString(0, "This is a test"); + auto map = builder.build(); + + EXPECT_EQ(map.getString(0), "This is a test"); +} + +TEST(MapBufferTest, testUTFStringEntry) { + auto builder = MapBufferBuilder(); + + builder.putString(0, "Let's count: 的, 一, 是"); + auto map = builder.build(); + + EXPECT_EQ(map.getString(0), "Let's count: 的, 一, 是"); +} + +TEST(MapBufferTest, testUTFStringEntries) { + auto builder = MapBufferBuilder(); + + builder.putString(0, "Let's count: 的, 一, 是"); + builder.putString(1, "This is a test"); + auto map = builder.build(); + + EXPECT_EQ(map.getString(0), "Let's count: 的, 一, 是"); + EXPECT_EQ(map.getString(1), "This is a test"); +} + +TEST(MapBufferTest, testEmptyMap) { + auto builder = MapBufferBuilder(); + auto map = builder.build(); + EXPECT_EQ(map.getCount(), 0); +} + +TEST(MapBufferTest, testEmptyMapConstant) { + auto map = MapBufferBuilder::EMPTY(); + EXPECT_EQ(map.getCount(), 0); +} + +TEST(MapBufferTest, testMapEntries) { + auto builder = MapBufferBuilder(); + builder.putString(0, "This is a test"); + builder.putInt(1, 1234); + auto map = builder.build(); + + EXPECT_EQ(map.getCount(), 2); + EXPECT_EQ(map.getString(0), "This is a test"); + EXPECT_EQ(map.getInt(1), 1234); + + auto builder2 = MapBufferBuilder(); + builder2.putInt(0, 4321); + builder2.putMapBuffer(1, map); + auto map2 = builder2.build(); + + EXPECT_EQ(map2.getCount(), 2); + EXPECT_EQ(map2.getInt(0), 4321); + + MapBuffer readMap2 = map2.getMapBuffer(1); + + EXPECT_EQ(readMap2.getCount(), 2); + EXPECT_EQ(readMap2.getString(0), "This is a test"); + EXPECT_EQ(readMap2.getInt(1), 1234); } diff --git a/android/ReactCommon/react/renderer/mounting/Android.mk b/android/ReactCommon/react/renderer/mounting/Android.mk index a4d138d942eb1..efd902e42cad9 100644 --- a/android/ReactCommon/react/renderer/mounting/Android.mk +++ b/android/ReactCommon/react/renderer/mounting/Android.mk @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libbetter libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug libreact_render_components_view libreact_render_components_root libreact_utils +LOCAL_SHARED_LIBRARIES := libjsi libbetter libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug librrc_view librrc_root libreact_utils libreact_debug libreact_render_graphics libreact_render_telemetry include $(BUILD_SHARED_LIBRARY) @@ -34,4 +34,7 @@ $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/core) $(call import-module,react/renderer/debug) $(call import-module,react/utils) +$(call import-module,react/debug) $(call import-module,yogajni) +$(call import-module,react/renderer/graphics) +$(call import-module,react/renderer/telemetry) diff --git a/android/ReactCommon/react/renderer/mounting/BUCK b/android/ReactCommon/react/renderer/mounting/BUCK index fcc289124b8ea..20a434f8687e7 100644 --- a/android/ReactCommon/react/renderer/mounting/BUCK +++ b/android/ReactCommon/react/renderer/mounting/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -32,12 +32,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/mounting", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -57,10 +51,12 @@ rn_xplat_cxx_library( "//xplat/folly:memory", "//xplat/folly:molly", react_native_xplat_target("better:better"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/components/root:root"), react_native_xplat_target("react/renderer/components/view:view"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), + react_native_xplat_target("react/renderer/telemetry:telemetry"), react_native_xplat_target("react/utils:utils"), ], ) @@ -72,7 +68,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], @@ -86,5 +82,6 @@ fb_xplat_cxx_test( react_native_xplat_target("react/renderer/components/root:root"), react_native_xplat_target("react/renderer/components/view:view"), react_native_xplat_target("react/renderer/components/scrollview:scrollview"), + react_native_xplat_target("react/test_utils:test_utils"), ], ) diff --git a/android/ReactCommon/react/renderer/mounting/Differentiator.cpp b/android/ReactCommon/react/renderer/mounting/Differentiator.cpp index 1bf8fe6e40d8f..a37e90758fcb1 100644 --- a/android/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/android/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -9,22 +9,35 @@ #include #include +#include #include #include #include #include "ShadowView.h" -// Uncomment this to enable verbose diffing logs, which can be useful for -// debugging. -// #define DEBUG_LOGS_DIFFER - #ifdef DEBUG_LOGS_DIFFER #include +#define DEBUG_LOGS_BREADCRUMBS 1 #define DEBUG_LOGS(code) code #else #define DEBUG_LOGS(code) #endif +#ifdef DEBUG_LOGS_BREADCRUMBS +#define BREADCRUMB_TYPE std::string +#define DIFF_BREADCRUMB(X) (breadcrumb + " - " + std::string(X)) +#define CREATE_DIFF_BREADCRUMB(X) std::to_string(X) +#else + +enum class NoBreadcrumb {}; + +#define BREADCRUMB_TYPE NoBreadcrumb const & +#define DIFF_BREADCRUMB(X) \ + {} +#define CREATE_DIFF_BREADCRUMB(X) \ + {} +#endif + namespace facebook { namespace react { @@ -86,7 +99,7 @@ class TinyMap final { inline Iterator find(KeyT key) { cleanVector(); - assert(key != 0); + react_native_assert(key != 0); if (begin_() == nullptr) { return end(); @@ -102,7 +115,7 @@ class TinyMap final { } inline void insert(Pair pair) { - assert(pair.first != 0); + react_native_assert(pair.first != 0); vector_.push_back(pair); } @@ -156,30 +169,31 @@ class TinyMap final { } better::small_vector vector_; - int numErased_{0}; - int erasedAtFront_{0}; + size_t numErased_{0}; + size_t erasedAtFront_{0}; }; /* * Sorting comparator for `reorderInPlaceIfNeeded`. */ static bool shouldFirstPairComesBeforeSecondOne( - ShadowViewNodePair const &lhs, - ShadowViewNodePair const &rhs) noexcept { - return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex(); + ShadowViewNodePair const *lhs, + ShadowViewNodePair const *rhs) noexcept { + return lhs->shadowNode->getOrderIndex() < rhs->shadowNode->getOrderIndex(); } /* * Reorders pairs in-place based on `orderIndex` using a stable sort algorithm. */ -static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { +static void reorderInPlaceIfNeeded( + ShadowViewNodePair::NonOwningList &pairs) noexcept { if (pairs.size() < 2) { return; } auto isReorderNeeded = false; for (auto const &pair : pairs) { - if (pair.shadowNode->getOrderIndex() != 0) { + if (pair->shadowNode->getOrderIndex() != 0) { isReorderNeeded = true; break; } @@ -193,8 +207,13 @@ static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept { pairs.begin(), pairs.end(), &shouldFirstPairComesBeforeSecondOne); } +static inline bool shadowNodeIsConcrete(ShadowNode const &shadowNode) { + return shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView); +} + static void sliceChildShadowNodeViewPairsRecursivelyV2( - ShadowViewNodePair::List &pairList, + ShadowViewNodePair::NonOwningList &pairList, + ViewNodePairScope &scope, Point layoutOffset, ShadowNode const &shadowNode) { for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { @@ -218,25 +237,34 @@ static void sliceChildShadowNodeViewPairsRecursivelyV2( // This might not be a FormsView, or a FormsStackingContext. We let the // differ handle removal of flattened views from the Mounting layer and // shuffling their children around. - bool isConcreteView = - childShadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView); + bool isConcreteView = shadowNodeIsConcrete(childShadowNode); bool areChildrenFlattened = !childShadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext); - pairList.push_back( - {shadowView, &childShadowNode, areChildrenFlattened, isConcreteView}); - - if (!childShadowNode.getTraits().check( - ShadowNodeTraits::Trait::FormsStackingContext)) { + Point storedOrigin = {}; + if (areChildrenFlattened) { + storedOrigin = origin; + } + scope.push_back( + {shadowView, + &childShadowNode, + areChildrenFlattened, + isConcreteView, + storedOrigin}); + pairList.push_back(&scope.back()); + + if (areChildrenFlattened) { sliceChildShadowNodeViewPairsRecursivelyV2( - pairList, origin, childShadowNode); + pairList, scope, origin, childShadowNode); } } } -ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( +ShadowViewNodePair::NonOwningList sliceChildShadowNodeViewPairsV2( ShadowNode const &shadowNode, - bool allowFlattened) { - auto pairList = ShadowViewNodePair::List{}; + ViewNodePairScope &scope, + bool allowFlattened, + Point layoutOffset) { + auto pairList = ShadowViewNodePair::NonOwningList{}; if (!shadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext) && @@ -245,23 +273,41 @@ ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( return pairList; } - sliceChildShadowNodeViewPairsRecursivelyV2(pairList, {0, 0}, shadowNode); + sliceChildShadowNodeViewPairsRecursivelyV2( + pairList, scope, layoutOffset, shadowNode); // Sorting pairs based on `orderIndex` if needed. reorderInPlaceIfNeeded(pairList); // Set list and mountIndex for each after reordering size_t mountIndex = 0; - for (auto &child : pairList) { - child.mountIndex = (child.isConcreteView ? mountIndex++ : -1); + for (auto child : pairList) { + child->mountIndex = (child->isConcreteView ? mountIndex++ : -1); } return pairList; } +/** + * Prefer calling this over `sliceChildShadowNodeViewPairsV2` directly, when + * possible. This can account for adding parent LayoutMetrics that are + * important to take into account, but tricky, in (un)flattening cases. + */ +static ShadowViewNodePair::NonOwningList +sliceChildShadowNodeViewPairsFromViewNodePair( + ShadowViewNodePair const &shadowViewNodePair, + ViewNodePairScope &scope, + bool allowFlattened = false) { + return sliceChildShadowNodeViewPairsV2( + *shadowViewNodePair.shadowNode, + scope, + allowFlattened, + shadowViewNodePair.contextOrigin); +} + /* - * Before we start to diff, let's make sure all our core data structures are in - * good shape to deliver the best performance. + * Before we start to diff, let's make sure all our core data structures are + * in good shape to deliver the best performance. */ static_assert( std::is_move_constructible::value, @@ -273,8 +319,8 @@ static_assert( std::is_move_constructible::value, "`ShadowViewNodePair` must be `move constructible`."); static_assert( - std::is_move_constructible::value, - "`ShadowViewNodePair::List` must be `move constructible`."); + std::is_move_constructible::value, + "`ShadowViewNodePair::NonOwningList` must be `move constructible`."); static_assert( std::is_move_assignable::value, @@ -286,29 +332,52 @@ static_assert( std::is_move_assignable::value, "`ShadowViewNodePair` must be `move assignable`."); static_assert( - std::is_move_assignable::value, - "`ShadowViewNodePair::List` must be `move assignable`."); + std::is_move_assignable::value, + "`ShadowViewNodePair::NonOwningList` must be `move assignable`."); -// Forward declaration static void calculateShadowViewMutationsV2( + BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs); + ShadowViewNodePair::NonOwningList &&oldChildPairs, + ShadowViewNodePair::NonOwningList &&newChildPairs); struct OrderedMutationInstructionContainer { - ShadowViewMutation::List &createMutations; - ShadowViewMutation::List &deleteMutations; - ShadowViewMutation::List &insertMutations; - ShadowViewMutation::List &removeMutations; - ShadowViewMutation::List &updateMutations; - ShadowViewMutation::List &downwardMutations; - ShadowViewMutation::List &destructiveDownwardMutations; + ShadowViewMutation::List createMutations{}; + ShadowViewMutation::List deleteMutations{}; + ShadowViewMutation::List insertMutations{}; + ShadowViewMutation::List removeMutations{}; + ShadowViewMutation::List updateMutations{}; + ShadowViewMutation::List downwardMutations{}; + ShadowViewMutation::List destructiveDownwardMutations{}; }; +static void updateMatchedPairSubtrees( + BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, + OrderedMutationInstructionContainer &mutationContainer, + TinyMap &newRemainingPairs, + ShadowViewNodePair::NonOwningList &oldChildPairs, + ShadowViewNodePair::NonOwningList &newChildPairs, + ShadowView const &parentShadowView, + ShadowViewNodePair const &oldPair, + ShadowViewNodePair const &newPair); + +static void updateMatchedPair( + BREADCRUMB_TYPE breadcrumb, + OrderedMutationInstructionContainer &mutationContainer, + bool oldNodeFoundInOrder, + bool newNodeFoundInOrder, + ShadowView const &parentShadowView, + ShadowViewNodePair const &oldPair, + ShadowViewNodePair const &newPair); + static void calculateShadowViewMutationsFlattener( + BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ReparentMode reparentMode, - OrderedMutationInstructionContainer &mutationInstructionContainer, + OrderedMutationInstructionContainer &mutationContainer, ShadowView const &parentShadowView, TinyMap &unvisitedFlattenedNodes, ShadowViewNodePair const &node, @@ -316,15 +385,210 @@ static void calculateShadowViewMutationsFlattener( TinyMap *parentSubVisitedOtherOldNodes = nullptr); +/** + * Updates the subtrees of any matched ShadowViewNodePair. This handles + * all cases of flattening/unflattening. + * + * This may modify data-structures passed to it and owned by the caller, + * specifically `newRemainingPairs`, and so the caller must also own + * the ViewNodePairScope used within. + */ +static void updateMatchedPairSubtrees( + BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, + OrderedMutationInstructionContainer &mutationContainer, + TinyMap &newRemainingPairs, + ShadowViewNodePair::NonOwningList &oldChildPairs, + ShadowViewNodePair::NonOwningList &newChildPairs, + ShadowView const &parentShadowView, + ShadowViewNodePair const &oldPair, + ShadowViewNodePair const &newPair) { + // Are we flattening or unflattening either one? If node was + // flattened in both trees, there's no change, just continue. + if (oldPair.flattened && newPair.flattened) { + return; + } + + // We are either flattening or unflattening this node. + if (oldPair.flattened != newPair.flattened) { + DEBUG_LOGS({ + LOG(ERROR) + << "Differ: flattening or unflattening in updateMatchedPairSubtrees: [" + << oldPair.shadowView.tag << "] [" << newPair.shadowView.tag << "] " + << oldPair.flattened << " " << newPair.flattened << " with parent: [" + << parentShadowView.tag << "]"; + }); + + // Flattening + if (!oldPair.flattened) { + // Flatten old tree into new list + // At the end of this loop we still want to know which of these + // children are visited, so we reuse the `newRemainingPairs` + // map. + calculateShadowViewMutationsFlattener( + DIFF_BREADCRUMB( + "Flatten tree " + std::to_string(parentShadowView.tag) + + " into list " + std::to_string(oldPair.shadowView.tag)), + scope, + ReparentMode::Flatten, + mutationContainer, + parentShadowView, + newRemainingPairs, + oldPair); + } + // Unflattening + else { + // Construct unvisited nodes map + auto unvisitedOldChildPairs = TinyMap{}; + // We don't know where all the children of oldChildPair are + // within oldChildPairs, but we know that they're in the same + // relative order. The reason for this is because of flattening + // + zIndex: the children could be listed before the parent, + // interwoven with children from other nodes, etc. + auto oldFlattenedNodes = + sliceChildShadowNodeViewPairsFromViewNodePair(oldPair, scope, true); + for (size_t i = 0, j = 0; + i < oldChildPairs.size() && j < oldFlattenedNodes.size(); + i++) { + auto &oldChild = *oldChildPairs[i]; + if (oldChild.shadowView.tag == oldFlattenedNodes[j]->shadowView.tag) { + unvisitedOldChildPairs.insert({oldChild.shadowView.tag, &oldChild}); + j++; + } + } + + // Unflatten old list into new tree + calculateShadowViewMutationsFlattener( + DIFF_BREADCRUMB( + "Unflatten old list " + std::to_string(parentShadowView.tag) + + " into new tree " + std::to_string(newPair.shadowView.tag)), + scope, + ReparentMode::Unflatten, + mutationContainer, + parentShadowView, + unvisitedOldChildPairs, + newPair); + + // If old nodes were not visited, we know that we can delete + // them now. They will be removed from the hierarchy by the + // outermost loop of this function. + // TODO: is this necessary anymore? + for (auto &oldFlattenedNodePtr : oldFlattenedNodes) { + auto &oldFlattenedNode = *oldFlattenedNodePtr; + auto unvisitedOldChildPairIt = + unvisitedOldChildPairs.find(oldFlattenedNode.shadowView.tag); + if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { + // Node was visited - make sure to remove it from + // "newRemainingPairs" map + auto newRemainingIt = + newRemainingPairs.find(oldFlattenedNode.shadowView.tag); + if (newRemainingIt != newRemainingPairs.end()) { + newRemainingPairs.erase(newRemainingIt); + } + } + } + } + + return; + } + + // Update subtrees if View is not flattened, and if node addresses + // are not equal + if (oldPair.shadowNode != newPair.shadowNode) { + ViewNodePairScope innerScope{}; + auto oldGrandChildPairs = + sliceChildShadowNodeViewPairsFromViewNodePair(oldPair, innerScope); + auto newGrandChildPairs = + sliceChildShadowNodeViewPairsFromViewNodePair(newPair, innerScope); + calculateShadowViewMutationsV2( + DIFF_BREADCRUMB( + "Non-trivial update " + std::to_string(oldPair.shadowView.tag)), + innerScope, + *(newGrandChildPairs.size() + ? &mutationContainer.downwardMutations + : &mutationContainer.destructiveDownwardMutations), + oldPair.shadowView, + std::move(oldGrandChildPairs), + std::move(newGrandChildPairs)); + } +} + +/** + * Handle updates to a matched node pair, but NOT to their subtrees. + * + * Here we have (and need) knowledge of whether a node was found during + * in-order traversal, or out-of-order via a map lookup. Nodes are only REMOVEd + * or INSERTed when they are encountered via in-order-traversal, to ensure + * correct ordering of INSERT and REMOVE mutations. + */ +static void updateMatchedPair( + BREADCRUMB_TYPE breadcrumb, + OrderedMutationInstructionContainer &mutationContainer, + bool oldNodeFoundInOrder, + bool newNodeFoundInOrder, + ShadowView const &parentShadowView, + ShadowViewNodePair const &oldPair, + ShadowViewNodePair const &newPair) { + oldPair.otherTreePair = &newPair; + newPair.otherTreePair = &oldPair; + + // Check concrete-ness of views + // Create/Delete and Insert/Remove if necessary + if (oldPair.isConcreteView != newPair.isConcreteView) { + if (newPair.isConcreteView) { + if (newNodeFoundInOrder) { + mutationContainer.insertMutations.push_back( + ShadowViewMutation::InsertMutation( + parentShadowView, + newPair.shadowView, + static_cast(newPair.mountIndex))); + } + mutationContainer.createMutations.push_back( + ShadowViewMutation::CreateMutation(newPair.shadowView)); + } else { + if (oldNodeFoundInOrder) { + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + parentShadowView, + oldPair.shadowView, + static_cast(oldPair.mountIndex))); + } + mutationContainer.deleteMutations.push_back( + ShadowViewMutation::DeleteMutation(oldPair.shadowView)); + } + } else if (oldPair.isConcreteView && newPair.isConcreteView) { + // If we found the old node by traversing, but not the new node, + // it means that there's some reordering requiring a REMOVE mutation. + if (oldNodeFoundInOrder && !newNodeFoundInOrder) { + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + parentShadowView, + newPair.shadowView, + static_cast(oldPair.mountIndex))); + } + + // Even if node's children are flattened, it might still be a + // concrete view. The case where they're different is handled + // above. + if (oldPair.shadowView != newPair.shadowView) { + mutationContainer.updateMutations.push_back( + ShadowViewMutation::UpdateMutation( + oldPair.shadowView, newPair.shadowView)); + } + } +} + /** * Here we flatten or unflatten a subtree, given an unflattened node in either * the old or new tree, and a list of flattened nodes in the other tree. * - * For example: if you are Flattening, the node will be in the old tree and the + * For example: if you are Flattening, the node will be in the old tree and + the * list will be from the new tree. If you are Unflattening, the opposite is true. - * It is currently not possible for ReactJS, and therefore React Native, to move + * It is currently not possible for ReactJS, and therefore React Native, to + move * a node *from* one parent to another without an entirely new subtree being * created. When we "reparent" in React Native here it is only because intermediate @@ -335,7 +599,8 @@ static void calculateShadowViewMutationsFlattener( * expanding vertically in that way. * Sketch of algorithm: - * 0. Create a map of nodes in the flattened list. This should be done *before* + * 0. Create a map of nodes in the flattened list. This should be done + *before* * calling this function. * 1. Traverse the Node Subtree; remove elements from the map as they are * visited in the tree. @@ -346,13 +611,16 @@ static void calculateShadowViewMutationsFlattener( * View if we're flattening. * If a node is in the list but not the map, it means it's been visited and * Update has already been - * performed in the subtree. If it *is* in the map, it means the node is not + * performed in the subtree. If it *is* in the map, it means the node is + not * * in the Tree, and should be Deleted/Created * **after this function is called**, by the caller. */ static void calculateShadowViewMutationsFlattener( + BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ReparentMode reparentMode, - OrderedMutationInstructionContainer &mutationInstructionContainer, + OrderedMutationInstructionContainer &mutationContainer, ShadowView const &parentShadowView, TinyMap &unvisitedOtherNodes, ShadowViewNodePair const &node, @@ -366,8 +634,8 @@ static void calculateShadowViewMutationsFlattener( }); // Step 1: iterate through entire tree - ShadowViewNodePair::List treeChildren = - sliceChildShadowNodeViewPairsV2(*node.shadowNode); + ShadowViewNodePair::NonOwningList treeChildren = + sliceChildShadowNodeViewPairsFromViewNodePair(node, scope); DEBUG_LOGS({ LOG(ERROR) << "Differ Flattener 1.4: " @@ -377,9 +645,9 @@ static void calculateShadowViewMutationsFlattener( LOG(ERROR) << "Differ Flattener Entry: Child Pairs: "; std::string strTreeChildPairs; for (size_t k = 0; k < treeChildren.size(); k++) { - strTreeChildPairs.append(std::to_string(treeChildren[k].shadowView.tag)); - strTreeChildPairs.append(treeChildren[k].isConcreteView ? "" : "'"); - strTreeChildPairs.append(treeChildren[k].flattened ? "*" : ""); + strTreeChildPairs.append(std::to_string(treeChildren[k]->shadowView.tag)); + strTreeChildPairs.append(treeChildren[k]->isConcreteView ? "" : "'"); + strTreeChildPairs.append(treeChildren[k]->flattened ? "*" : ""); strTreeChildPairs.append(", "); } std::string strListChildPairs; @@ -396,7 +664,8 @@ static void calculateShadowViewMutationsFlattener( << strListChildPairs; }); - // Views in other tree that are visited by sub-flattening or sub-unflattening + // Views in other tree that are visited by sub-flattening or + // sub-unflattening TinyMap subVisitedOtherNewNodes{}; TinyMap subVisitedOtherOldNodes{}; auto subVisitedNewMap = @@ -413,28 +682,7 @@ static void calculateShadowViewMutationsFlattener( for (size_t index = 0; index < treeChildren.size() && index < treeChildren.size(); index++) { - // First, remove all children of the tree being flattened, or insert - // children into parent tree if they're being unflattened. Then, look up - // each node in the "unvisited" list and update the nodes and subtrees if - // appropriate. - auto &treeChildPair = treeChildren[index]; - - // Caller will take care of the corresponding action in the other tree. - if (treeChildPair.isConcreteView) { - if (reparentMode == ReparentMode::Flatten) { - mutationInstructionContainer.removeMutations.push_back( - ShadowViewMutation::RemoveMutation( - node.shadowView, - treeChildPair.shadowView, - treeChildPair.mountIndex)); - } else { - mutationInstructionContainer.insertMutations.push_back( - ShadowViewMutation::InsertMutation( - node.shadowView, - treeChildPair.shadowView, - treeChildPair.mountIndex)); - } - } + auto &treeChildPair = *treeChildren[index]; // Try to find node in other tree auto unvisitedIt = unvisitedOtherNodes.find(treeChildPair.shadowView.tag); @@ -443,14 +691,129 @@ static void calculateShadowViewMutationsFlattener( ? subVisitedNewMap->find(treeChildPair.shadowView.tag) : subVisitedNewMap->end()); auto subVisitedOtherOldIt = - (unvisitedIt == unvisitedOtherNodes.end() + (unvisitedIt == unvisitedOtherNodes.end() && subVisitedNewMap->end() ? subVisitedOldMap->find(treeChildPair.shadowView.tag) : subVisitedOldMap->end()); - // Find in other tree - if (unvisitedIt != unvisitedOtherNodes.end() || + bool existsInOtherTree = unvisitedIt != unvisitedOtherNodes.end() || subVisitedOtherNewIt != subVisitedNewMap->end() || - subVisitedOtherOldIt != subVisitedOldMap->end()) { + subVisitedOtherOldIt != subVisitedOldMap->end(); + + auto otherTreeNodePairPtr = + (existsInOtherTree + ? (unvisitedIt != unvisitedOtherNodes.end() + ? unvisitedIt->second + : (subVisitedOtherNewIt != subVisitedNewMap->end() + ? subVisitedOtherNewIt->second + : subVisitedOtherOldIt->second)) + : nullptr); + + react_native_assert( + !existsInOtherTree || + (unvisitedIt != unvisitedOtherNodes.end() || + subVisitedOtherNewIt != subVisitedNewMap->end() || + subVisitedOtherOldIt != subVisitedOldMap->end())); + react_native_assert( + unvisitedIt == unvisitedOtherNodes.end() || + unvisitedIt->second->shadowView.tag == treeChildPair.shadowView.tag); + react_native_assert( + subVisitedOtherNewIt == subVisitedNewMap->end() || + subVisitedOtherNewIt->second->shadowView.tag == + treeChildPair.shadowView.tag); + react_native_assert( + subVisitedOtherOldIt == subVisitedOldMap->end() || + subVisitedOtherOldIt->second->shadowView.tag == + treeChildPair.shadowView.tag); + + bool alreadyUpdated = false; + + // Find in other tree and updated `otherTreePair` pointers + if (existsInOtherTree) { + react_native_assert(otherTreeNodePairPtr != nullptr); + auto newTreeNodePair = + (reparentMode == ReparentMode::Flatten ? otherTreeNodePairPtr + : &treeChildPair); + auto oldTreeNodePair = + (reparentMode == ReparentMode::Flatten ? &treeChildPair + : otherTreeNodePairPtr); + + react_native_assert(newTreeNodePair->shadowView.tag != 0); + react_native_assert(oldTreeNodePair->shadowView.tag != 0); + react_native_assert( + oldTreeNodePair->shadowView.tag == newTreeNodePair->shadowView.tag); + + alreadyUpdated = + newTreeNodePair->inOtherTree() || oldTreeNodePair->inOtherTree(); + + // We want to update these values unconditionally. Always do this + // before hitting any "continue" statements. + newTreeNodePair->otherTreePair = oldTreeNodePair; + oldTreeNodePair->otherTreePair = newTreeNodePair; + react_native_assert(treeChildPair.otherTreePair != nullptr); + } + + // Remove all children (non-recursively) of tree being flattened, or + // insert children into parent tree if they're being unflattened. + // Caller will take care of the corresponding action in the other tree + // (caller will handle DELETE case if we REMOVE here; caller will handle + // CREATE case if we INSERT here). + if (treeChildPair.isConcreteView) { + if (reparentMode == ReparentMode::Flatten) { + // treeChildPair.shadowView represents the "old" view in this case. + // If there's a "new" view, an UPDATE new -> old will be generated + // and will be executed before the REMOVE. Thus, we must actually + // perform a REMOVE (new view) FROM (old index) in this case so that + // we don't hit asserts in StubViewTree's REMOVE path. + // We also only do this if the "other" (newer) view is concrete. If + // it's not concrete, there will be no UPDATE mutation. + react_native_assert(existsInOtherTree == treeChildPair.inOtherTree()); + if (treeChildPair.inOtherTree() && + treeChildPair.otherTreePair->isConcreteView) { + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + node.shadowView, + treeChildPair.otherTreePair->shadowView, + static_cast(treeChildPair.mountIndex))); + } else { + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + node.shadowView, + treeChildPair.shadowView, + static_cast(treeChildPair.mountIndex))); + } + } else { + // treeChildParent represents the "new" version of the node, so + // we can safely insert it without checking in the other tree + mutationContainer.insertMutations.push_back( + ShadowViewMutation::InsertMutation( + node.shadowView, + treeChildPair.shadowView, + static_cast(treeChildPair.mountIndex))); + } + } + + // Find in other tree + if (existsInOtherTree) { + react_native_assert(otherTreeNodePairPtr != nullptr); + auto &otherTreeNodePair = *otherTreeNodePairPtr; + + auto &newTreeNodePair = + (reparentMode == ReparentMode::Flatten ? otherTreeNodePair + : treeChildPair); + auto &oldTreeNodePair = + (reparentMode == ReparentMode::Flatten ? treeChildPair + : otherTreeNodePair); + + react_native_assert(newTreeNodePair.shadowView.tag != 0); + react_native_assert(oldTreeNodePair.shadowView.tag != 0); + react_native_assert( + oldTreeNodePair.shadowView.tag == newTreeNodePair.shadowView.tag); + + // If we've already done updates, don't repeat it. + if (alreadyUpdated) { + continue; + } + // If we've already done updates on this node, don't repeat. if (reparentMode == ReparentMode::Flatten && unvisitedIt == unvisitedOtherNodes.end() && @@ -463,47 +826,36 @@ static void calculateShadowViewMutationsFlattener( continue; } - auto &otherTreeNodePair = - *(unvisitedIt != unvisitedOtherNodes.end() - ? unvisitedIt->second - : (subVisitedOtherNewIt != subVisitedNewMap->end() - ? subVisitedOtherNewIt->second - : subVisitedOtherOldIt->second)); - - // If we've already done updates, don't repeat it. - if (treeChildPair.inOtherTree || otherTreeNodePair.inOtherTree) { - continue; - } - - auto &newTreeNodePair = - (reparentMode == ReparentMode::Flatten ? otherTreeNodePair - : treeChildPair); - auto &oldTreeNodePair = - (reparentMode == ReparentMode::Flatten ? treeChildPair - : otherTreeNodePair); - + // TODO: compare ShadowNode pointer instead of ShadowView here? + // Or ShadowNode ptr comparison before comparing ShadowView, to allow for + // short-circuiting? ShadowView comparison is relatively expensive vs + // ShadowNode. if (newTreeNodePair.shadowView != oldTreeNodePair.shadowView && newTreeNodePair.isConcreteView && oldTreeNodePair.isConcreteView) { - mutationInstructionContainer.updateMutations.push_back( + mutationContainer.updateMutations.push_back( ShadowViewMutation::UpdateMutation( - parentShadowView, - oldTreeNodePair.shadowView, - newTreeNodePair.shadowView, - newTreeNodePair.mountIndex)); + oldTreeNodePair.shadowView, newTreeNodePair.shadowView)); } // Update children if appropriate. if (!oldTreeNodePair.flattened && !newTreeNodePair.flattened) { if (oldTreeNodePair.shadowNode != newTreeNodePair.shadowNode) { + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - mutationInstructionContainer.downwardMutations, + DIFF_BREADCRUMB( + "(Un)Flattener trivial update of " + + std::to_string(newTreeNodePair.shadowView.tag)), + innerScope, + mutationContainer.downwardMutations, newTreeNodePair.shadowView, - sliceChildShadowNodeViewPairsV2(*oldTreeNodePair.shadowNode), - sliceChildShadowNodeViewPairsV2(*newTreeNodePair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + oldTreeNodePair, innerScope), + sliceChildShadowNodeViewPairsFromViewNodePair( + newTreeNodePair, innerScope)); } } else if (oldTreeNodePair.flattened != newTreeNodePair.flattened) { - // We need to handle one of the children being flattened or unflattened, - // in the context of a parent flattening or unflattening. + // We need to handle one of the children being flattened or + // unflattened, in the context of a parent flattening or unflattening. ReparentMode childReparentMode = (oldTreeNodePair.flattened ? ReparentMode::Unflatten : ReparentMode::Flatten); @@ -512,8 +864,20 @@ static void calculateShadowViewMutationsFlattener( // This is a flatten-flatten, or unflatten-unflatten. if (childReparentMode == reparentMode) { calculateShadowViewMutationsFlattener( + DIFF_BREADCRUMB( + std::string( + reparentMode == ReparentMode::Flatten + ? "Flatten-Flatten" + : "Unflatten-Unflatten") + + " new:" + + std::to_string( + reparentMode == ReparentMode::Flatten + ? parentShadowView.tag + : newTreeNodePair.shadowView.tag) + + " old:" + std::to_string(treeChildPair.shadowView.tag)), + scope, childReparentMode, - mutationInstructionContainer, + mutationContainer, (reparentMode == ReparentMode::Flatten ? parentShadowView : newTreeNodePair.shadowView), @@ -522,89 +886,72 @@ static void calculateShadowViewMutationsFlattener( subVisitedNewMap, subVisitedOldMap); } else { - // Unflatten parent, flatten child - if (childReparentMode == ReparentMode::Flatten) { - // Construct unvisited nodes map - auto unvisitedNewChildPairs = TinyMap{}; - // Memory note: these oldFlattenedNodes all disappear at the end of - // this "else" block, including any annotations we put on them. - auto newFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *newTreeNodePair.shadowNode, true); - for (size_t i = 0; i < newFlattenedNodes.size(); i++) { - auto &newChild = newFlattenedNodes[i]; - - auto unvisitedOtherNodesIt = - unvisitedOtherNodes.find(newChild.shadowView.tag); - if (unvisitedOtherNodesIt != unvisitedOtherNodes.end()) { - auto &unvisitedItPair = *unvisitedOtherNodesIt->second; - unvisitedNewChildPairs.insert( - {unvisitedItPair.shadowView.tag, &unvisitedItPair}); - } else { - unvisitedNewChildPairs.insert( - {newChild.shadowView.tag, &newChild}); - } + // Get flattened nodes from either new or old tree + auto flattenedNodes = sliceChildShadowNodeViewPairsFromViewNodePair( + (childReparentMode == ReparentMode::Flatten ? newTreeNodePair + : oldTreeNodePair), + scope, + true); + // Construct unvisited nodes map + auto unvisitedRecursiveChildPairs = + TinyMap{}; + for (size_t i = 0; i < flattenedNodes.size(); i++) { + auto &newChild = *flattenedNodes[i]; + + auto unvisitedOtherNodesIt = + unvisitedOtherNodes.find(newChild.shadowView.tag); + if (unvisitedOtherNodesIt != unvisitedOtherNodes.end()) { + auto unvisitedItPair = *unvisitedOtherNodesIt->second; + unvisitedRecursiveChildPairs.insert( + {unvisitedItPair.shadowView.tag, &unvisitedItPair}); + } else { + unvisitedRecursiveChildPairs.insert( + {newChild.shadowView.tag, &newChild}); } + } + // Unflatten parent, flatten child + if (childReparentMode == ReparentMode::Flatten) { // Flatten old tree into new list // At the end of this loop we still want to know which of these // children are visited, so we reuse the `newRemainingPairs` map. calculateShadowViewMutationsFlattener( + DIFF_BREADCRUMB( + std::string("Flatten old tree into new list; new:") + + std::to_string( + reparentMode == ReparentMode::Flatten + ? parentShadowView.tag + : newTreeNodePair.shadowView.tag) + + " old:" + std::to_string(oldTreeNodePair.shadowView.tag)), + scope, ReparentMode::Flatten, - mutationInstructionContainer, + mutationContainer, (reparentMode == ReparentMode::Flatten ? parentShadowView : newTreeNodePair.shadowView), - unvisitedNewChildPairs, + unvisitedRecursiveChildPairs, oldTreeNodePair, subVisitedNewMap, subVisitedOldMap); - - for (auto &newFlattenedNode : newFlattenedNodes) { - auto unvisitedOldChildPairIt = - unvisitedNewChildPairs.find(newFlattenedNode.shadowView.tag); - - if (unvisitedOldChildPairIt == unvisitedNewChildPairs.end()) { - // Node was visited. - - auto deleteCreateIt = deletionCreationCandidatePairs.find( - newFlattenedNode.shadowView.tag); - if (deleteCreateIt != deletionCreationCandidatePairs.end()) { - deletionCreationCandidatePairs.erase(deleteCreateIt); - } - } - } } // Flatten parent, unflatten child else { - // Construct unvisited nodes map - auto unvisitedOldChildPairs = TinyMap{}; - // Memory note: these oldFlattenedNodes all disappear at the end of - // this "else" block, including any annotations we put on them. - auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *oldTreeNodePair.shadowNode, true); - for (size_t i = 0; i < oldFlattenedNodes.size(); i++) { - auto &oldChild = oldFlattenedNodes[i]; - - auto unvisitedOtherNodesIt = - unvisitedOtherNodes.find(oldChild.shadowView.tag); - if (unvisitedOtherNodesIt != unvisitedOtherNodes.end()) { - auto &unvisitedItPair = *unvisitedOtherNodesIt->second; - unvisitedOldChildPairs.insert( - {unvisitedItPair.shadowView.tag, &unvisitedItPair}); - } else { - unvisitedOldChildPairs.insert( - {oldChild.shadowView.tag, &oldChild}); - } - } - // Unflatten old list into new tree calculateShadowViewMutationsFlattener( + DIFF_BREADCRUMB( + "Unflatten old list into new tree; old:" + + std::to_string( + reparentMode == ReparentMode::Flatten + ? parentShadowView.tag + : newTreeNodePair.shadowView.tag) + + " new:" + std::to_string(newTreeNodePair.shadowView.tag)), + scope, ReparentMode::Unflatten, - mutationInstructionContainer, + mutationContainer, (reparentMode == ReparentMode::Flatten ? parentShadowView : newTreeNodePair.shadowView), - unvisitedOldChildPairs, + unvisitedRecursiveChildPairs, newTreeNodePair, subVisitedNewMap, subVisitedOldMap); @@ -612,45 +959,24 @@ static void calculateShadowViewMutationsFlattener( // If old nodes were not visited, we know that we can delete them // now. They will be removed from the hierarchy by the outermost // loop of this function. - for (auto &oldFlattenedNode : oldFlattenedNodes) { - auto unvisitedOldChildPairIt = - unvisitedOldChildPairs.find(oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt != unvisitedOldChildPairs.end()) { - // Node unvisited - mark the entire subtree for deletion - if (oldFlattenedNode.isConcreteView) { - auto tag = oldFlattenedNode.shadowView.tag; - auto oldRemainingChildInListIt = std::find_if( - treeChildren.begin(), - treeChildren.end(), - [&tag](ShadowViewNodePair &nodePair) { - return nodePair.shadowView.tag == tag; - }); - if (oldRemainingChildInListIt != treeChildren.end()) { - auto deleteCreateIt = deletionCreationCandidatePairs.find( - oldFlattenedNode.shadowView.tag); - if (deleteCreateIt == - deletionCreationCandidatePairs.end()) { - deletionCreationCandidatePairs.insert( - {tag, &*oldRemainingChildInListIt}); - } - } else { - // TODO: we might want to remove this block. It seems - // impossible to hit this logically (and empirically, after - // testing on lots of randomized and pathologically - // constructed trees) but I'm leaving this here out of an - // abundance of caution. - mutationInstructionContainer.deleteMutations.push_back( - ShadowViewMutation::DeleteMutation( - oldFlattenedNode.shadowView)); - - calculateShadowViewMutationsV2( - mutationInstructionContainer - .destructiveDownwardMutations, - oldFlattenedNode.shadowView, - sliceChildShadowNodeViewPairsV2( - *oldFlattenedNode.shadowNode), - {}); - } + for (auto unvisitedOldChildPairIt = + unvisitedRecursiveChildPairs.begin(); + unvisitedOldChildPairIt != unvisitedRecursiveChildPairs.end(); + unvisitedOldChildPairIt++) { + if (unvisitedOldChildPairIt->first == 0) { + continue; + } + auto &oldFlattenedNode = *unvisitedOldChildPairIt->second; + + // Node unvisited - mark the entire subtree for deletion + if (oldFlattenedNode.isConcreteView && + !oldFlattenedNode.inOtherTree()) { + Tag tag = oldFlattenedNode.shadowView.tag; + auto deleteCreateIt = deletionCreationCandidatePairs.find( + oldFlattenedNode.shadowView.tag); + if (deleteCreateIt == deletionCreationCandidatePairs.end()) { + deletionCreationCandidatePairs.insert( + {tag, &oldFlattenedNode}); } } else { // Node was visited - make sure to remove it from @@ -660,13 +986,6 @@ static void calculateShadowViewMutationsFlattener( if (newRemainingIt != unvisitedOtherNodes.end()) { unvisitedOtherNodes.erase(newRemainingIt); } - - // We also remove it from delete/creation candidates - auto deleteCreateIt = deletionCreationCandidatePairs.find( - oldFlattenedNode.shadowView.tag); - if (deleteCreateIt != deletionCreationCandidatePairs.end()) { - deletionCreationCandidatePairs.erase(deleteCreateIt); - } } } } @@ -677,35 +996,23 @@ static void calculateShadowViewMutationsFlattener( // concrete view. Removing the node from the unvisited list prevents the // caller from taking further action on this node, so make sure to // delete/create if the Concreteness of the node has changed. - if (newTreeNodePair.isConcreteView != oldTreeNodePair.isConcreteView && - !newTreeNodePair.inOtherTree) { + if (newTreeNodePair.isConcreteView != oldTreeNodePair.isConcreteView) { if (newTreeNodePair.isConcreteView) { - mutationInstructionContainer.createMutations.push_back( + mutationContainer.createMutations.push_back( ShadowViewMutation::CreateMutation(newTreeNodePair.shadowView)); } else { - mutationInstructionContainer.deleteMutations.push_back( - ShadowViewMutation::DeleteMutation(newTreeNodePair.shadowView)); + mutationContainer.deleteMutations.push_back( + ShadowViewMutation::DeleteMutation(oldTreeNodePair.shadowView)); } } - treeChildPair.inOtherTree = true; - otherTreeNodePair.inOtherTree = true; - - if (parentSubVisitedOtherNewNodes != nullptr) { - parentSubVisitedOtherNewNodes->insert( - {newTreeNodePair.shadowView.tag, &newTreeNodePair}); - } - if (parentSubVisitedOtherOldNodes != nullptr) { - parentSubVisitedOtherOldNodes->insert( - {oldTreeNodePair.shadowView.tag, &oldTreeNodePair}); - } - - if (unvisitedIt != unvisitedOtherNodes.end()) { - unvisitedOtherNodes.erase(unvisitedIt); - } + subVisitedNewMap->insert( + {newTreeNodePair.shadowView.tag, &newTreeNodePair}); + subVisitedOldMap->insert( + {oldTreeNodePair.shadowView.tag, &oldTreeNodePair}); } else { // Node does not in exist in other tree. - if (treeChildPair.isConcreteView && !treeChildPair.inOtherTree) { + if (treeChildPair.isConcreteView && !treeChildPair.inOtherTree()) { auto deletionCreationIt = deletionCreationCandidatePairs.find(treeChildPair.shadowView.tag); if (deletionCreationIt == deletionCreationCandidatePairs.end()) { @@ -717,8 +1024,8 @@ static void calculateShadowViewMutationsFlattener( } // Final step: go through creation/deletion candidates and delete/create - // subtrees if they were never visited during the execution of the above loop - // and recursions. + // subtrees if they were never visited during the execution of the above + // loop and recursions. for (auto it = deletionCreationCandidatePairs.begin(); it != deletionCreationCandidatePairs.end(); it++) { @@ -727,42 +1034,60 @@ static void calculateShadowViewMutationsFlattener( } auto &treeChildPair = *it->second; - // If node was visited during a flattening/unflattening recursion. - if (treeChildPair.inOtherTree) { + // If node was visited during a flattening/unflattening recursion, + // and the node in the other tree is concrete, that means it was + // already created/deleted and we don't need to do that here. + // It is always the responsibility of the matcher to update subtrees when + // nodes are matched. + if (treeChildPair.inOtherTree()) { continue; } if (reparentMode == ReparentMode::Flatten) { - mutationInstructionContainer.deleteMutations.push_back( + mutationContainer.deleteMutations.push_back( ShadowViewMutation::DeleteMutation(treeChildPair.shadowView)); if (!treeChildPair.flattened) { + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - mutationInstructionContainer.destructiveDownwardMutations, + DIFF_BREADCRUMB( + "Recursively delete tree child pair (flatten case): " + + std::to_string(treeChildPair.shadowView.tag)), + innerScope, + mutationContainer.destructiveDownwardMutations, treeChildPair.shadowView, - sliceChildShadowNodeViewPairsV2(*treeChildPair.shadowNode), + sliceChildShadowNodeViewPairsFromViewNodePair( + treeChildPair, innerScope), {}); } } else { - mutationInstructionContainer.createMutations.push_back( + mutationContainer.createMutations.push_back( ShadowViewMutation::CreateMutation(treeChildPair.shadowView)); if (!treeChildPair.flattened) { + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - mutationInstructionContainer.downwardMutations, + DIFF_BREADCRUMB( + "Recursively delete tree child pair (unflatten case): " + + std::to_string(treeChildPair.shadowView.tag)), + innerScope, + mutationContainer.downwardMutations, treeChildPair.shadowView, {}, - sliceChildShadowNodeViewPairsV2(*treeChildPair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + treeChildPair, innerScope)); } } } } static void calculateShadowViewMutationsV2( + BREADCRUMB_TYPE breadcrumb, + ViewNodePairScope &scope, ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs) { + ShadowViewNodePair::NonOwningList &&oldChildPairs, + ShadowViewNodePair::NonOwningList &&newChildPairs) { if (oldChildPairs.empty() && newChildPairs.empty()) { return; } @@ -770,21 +1095,7 @@ static void calculateShadowViewMutationsV2( size_t index = 0; // Lists of mutations - auto createMutations = ShadowViewMutation::List{}; - auto deleteMutations = ShadowViewMutation::List{}; - auto insertMutations = ShadowViewMutation::List{}; - auto removeMutations = ShadowViewMutation::List{}; - auto updateMutations = ShadowViewMutation::List{}; - auto downwardMutations = ShadowViewMutation::List{}; - auto destructiveDownwardMutations = ShadowViewMutation::List{}; - auto mutationInstructionContainer = - OrderedMutationInstructionContainer{createMutations, - deleteMutations, - insertMutations, - removeMutations, - updateMutations, - downwardMutations, - destructiveDownwardMutations}; + auto mutationContainer = OrderedMutationInstructionContainer{}; DEBUG_LOGS({ LOG(ERROR) << "Differ Entry: Child Pairs of node: [" << parentShadowView.tag @@ -792,19 +1103,19 @@ static void calculateShadowViewMutationsV2( std::string strOldChildPairs; for (size_t oldIndex = 0; oldIndex < oldChildPairs.size(); oldIndex++) { strOldChildPairs.append( - std::to_string(oldChildPairs[oldIndex].shadowView.tag)); + std::to_string(oldChildPairs[oldIndex]->shadowView.tag)); strOldChildPairs.append( - oldChildPairs[oldIndex].isConcreteView ? "" : "'"); - strOldChildPairs.append(oldChildPairs[oldIndex].flattened ? "*" : ""); + oldChildPairs[oldIndex]->isConcreteView ? "" : "'"); + strOldChildPairs.append(oldChildPairs[oldIndex]->flattened ? "*" : ""); strOldChildPairs.append(", "); } std::string strNewChildPairs; for (size_t newIndex = 0; newIndex < newChildPairs.size(); newIndex++) { strNewChildPairs.append( - std::to_string(newChildPairs[newIndex].shadowView.tag)); + std::to_string(newChildPairs[newIndex]->shadowView.tag)); strNewChildPairs.append( - newChildPairs[newIndex].isConcreteView ? "" : "'"); - strNewChildPairs.append(newChildPairs[newIndex].flattened ? "*" : ""); + newChildPairs[newIndex]->isConcreteView ? "" : "'"); + strNewChildPairs.append(newChildPairs[newIndex]->flattened ? "*" : ""); strNewChildPairs.append(", "); } LOG(ERROR) << "Differ Entry: Old Child Pairs: " << strOldChildPairs; @@ -814,8 +1125,8 @@ static void calculateShadowViewMutationsV2( // Stage 1: Collecting `Update` mutations for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size(); index++) { - auto &oldChildPair = oldChildPairs[index]; - auto &newChildPair = newChildPairs[index]; + auto &oldChildPair = *oldChildPairs[index]; + auto &newChildPair = *newChildPairs[index]; if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) { DEBUG_LOGS({ @@ -829,8 +1140,8 @@ static void calculateShadowViewMutationsV2( break; } - // If either view was flattened, and that has changed this frame, don't try - // to update + // If either view was flattened, and that has changed this frame, don't + // try to update if (oldChildPair.flattened != newChildPair.flattened || oldChildPair.isConcreteView != newChildPair.isConcreteView) { break; @@ -849,23 +1160,27 @@ static void calculateShadowViewMutationsV2( if (newChildPair.isConcreteView && oldChildPair.shadowView != newChildPair.shadowView) { - updateMutations.push_back(ShadowViewMutation::UpdateMutation( - parentShadowView, - oldChildPair.shadowView, - newChildPair.shadowView, - newChildPair.mountIndex)); + mutationContainer.updateMutations.push_back( + ShadowViewMutation::UpdateMutation( + oldChildPair.shadowView, newChildPair.shadowView)); } // Recursively update tree if ShadowNode pointers are not equal if (!oldChildPair.flattened && oldChildPair.shadowNode != newChildPair.shadowNode) { - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode); + ViewNodePairScope innerScope{}; + auto oldGrandChildPairs = sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope); + auto newGrandChildPairs = sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope); calculateShadowViewMutationsV2( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), + DIFF_BREADCRUMB( + "Stage 1: Recurse on " + + std::to_string(oldChildPair.shadowView.tag)), + innerScope, + *(newGrandChildPairs.size() + ? &mutationContainer.downwardMutations + : &mutationContainer.destructiveDownwardMutations), oldChildPair.shadowView, std::move(oldGrandChildPairs), std::move(newGrandChildPairs)); @@ -878,7 +1193,7 @@ static void calculateShadowViewMutationsV2( // We've reached the end of the new children. We can delete+remove the // rest. for (; index < oldChildPairs.size(); index++) { - auto const &oldChildPair = oldChildPairs[index]; + auto const &oldChildPair = *oldChildPairs[index]; DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 2: Deleting Tag/Tree: [" @@ -890,24 +1205,32 @@ static void calculateShadowViewMutationsV2( continue; } - deleteMutations.push_back( + mutationContainer.deleteMutations.push_back( ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, oldChildPair.shadowView, oldChildPair.mountIndex)); + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + parentShadowView, + oldChildPair.shadowView, + static_cast(oldChildPair.mountIndex))); // We also have to call the algorithm recursively to clean up the entire // subtree starting from the removed view. + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - destructiveDownwardMutations, + DIFF_BREADCRUMB( + "Trivial delete " + std::to_string(oldChildPair.shadowView.tag)), + innerScope, + mutationContainer.destructiveDownwardMutations, oldChildPair.shadowView, - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode), + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope), {}); } } else if (index == oldChildPairs.size()) { // If we don't have any more existing children we can choose a fast path // since the rest will all be create+insert. for (; index < newChildPairs.size(); index++) { - auto const &newChildPair = newChildPairs[index]; + auto const &newChildPair = *newChildPairs[index]; DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 3: Creating Tag/Tree: [" @@ -919,16 +1242,24 @@ static void calculateShadowViewMutationsV2( continue; } - insertMutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, newChildPair.shadowView, newChildPair.mountIndex)); - createMutations.push_back( + mutationContainer.insertMutations.push_back( + ShadowViewMutation::InsertMutation( + parentShadowView, + newChildPair.shadowView, + static_cast(newChildPair.mountIndex))); + mutationContainer.createMutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - downwardMutations, + DIFF_BREADCRUMB( + "Trivial create " + std::to_string(newChildPair.shadowView.tag)), + innerScope, + mutationContainer.downwardMutations, newChildPair.shadowView, {}, - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope)); } } else { // Collect map of tags in the new list @@ -936,7 +1267,7 @@ static void calculateShadowViewMutationsV2( auto newInsertedPairs = TinyMap{}; auto deletionCandidatePairs = TinyMap{}; for (; index < newChildPairs.size(); index++) { - auto &newChildPair = newChildPairs[index]; + auto &newChildPair = *newChildPairs[index]; newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair}); } @@ -952,8 +1283,8 @@ static void calculateShadowViewMutationsV2( // Advance both pointers if pointing to the same element if (haveNewPair && haveOldPair) { - auto const &oldChildPair = oldChildPairs[oldIndex]; - auto const &newChildPair = newChildPairs[newIndex]; + auto const &oldChildPair = *oldChildPairs[oldIndex]; + auto const &newChildPair = *newChildPairs[newIndex]; Tag newTag = newChildPair.shadowView.tag; Tag oldTag = oldChildPair.shadowView.tag; @@ -971,142 +1302,29 @@ static void calculateShadowViewMutationsV2( << " with parent: [" << parentShadowView.tag << "]"; }); - // Check concrete-ness of views - // Create/Delete and Insert/Remove if necessary - if (oldChildPair.isConcreteView != newChildPair.isConcreteView) { - if (newChildPair.isConcreteView) { - insertMutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, - newChildPair.shadowView, - newChildPair.mountIndex)); - createMutations.push_back( - ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - } else { - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, - oldChildPair.shadowView, - oldChildPair.mountIndex)); - deleteMutations.push_back( - ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); - } - } else if ( - oldChildPair.isConcreteView && newChildPair.isConcreteView) { - // Even if node's children are flattened, it might still be a - // concrete view. The case where they're different is handled above. - if (oldChildPair.shadowView != newChildPair.shadowView) { - updateMutations.push_back(ShadowViewMutation::UpdateMutation( - parentShadowView, - oldChildPair.shadowView, - newChildPair.shadowView, - newChildPair.mountIndex)); - } - - // Remove from newRemainingPairs - auto newRemainingPairIt = newRemainingPairs.find(oldTag); - if (newRemainingPairIt != newRemainingPairs.end()) { - newRemainingPairs.erase(newRemainingPairIt); - } - } - - // Are we flattening or unflattening either one? If node was flattened - // in both trees, there's no change, just continue. - if (oldChildPair.flattened && newChildPair.flattened) { - newIndex++; - oldIndex++; - continue; - } - // We are either flattening or unflattening this node. - if (oldChildPair.flattened != newChildPair.flattened) { - DEBUG_LOGS({ - LOG(ERROR) << "Differ: flattening or unflattening at branch 6: [" - << oldChildPair.shadowView.tag << "] [" - << newChildPair.shadowView.tag << "] " - << oldChildPair.flattened << " " - << newChildPair.flattened << " with parent: [" - << parentShadowView.tag << "]"; - }); - - // Flattening - if (!oldChildPair.flattened) { - // Flatten old tree into new list - // At the end of this loop we still want to know which of these - // children are visited, so we reuse the `newRemainingPairs` map. - calculateShadowViewMutationsFlattener( - ReparentMode::Flatten, - mutationInstructionContainer, - parentShadowView, - newRemainingPairs, - oldChildPair); - } - // Unflattening - else { - // Construct unvisited nodes map - auto unvisitedOldChildPairs = - TinyMap{}; - // We don't know where all the children of oldChildPair are within - // oldChildPairs, but we know that they're in the same relative - // order. The reason for this is because of flattening + zIndex: - // the children could be listed before the parent, interwoven with - // children from other nodes, etc. - auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *oldChildPair.shadowNode, true); - for (size_t i = 0, j = 0; - i < oldChildPairs.size() && j < oldFlattenedNodes.size(); - i++) { - auto &oldChild = oldChildPairs[i]; - if (oldChild.shadowView.tag == - oldFlattenedNodes[j].shadowView.tag) { - unvisitedOldChildPairs.insert( - {oldChild.shadowView.tag, &oldChild}); - j++; - } - } - - // Unflatten old list into new tree - calculateShadowViewMutationsFlattener( - ReparentMode::Unflatten, - mutationInstructionContainer, - parentShadowView, - unvisitedOldChildPairs, - newChildPair); - - // If old nodes were not visited, we know that we can delete them - // now. They will be removed from the hierarchy by the outermost - // loop of this function. - for (auto &oldFlattenedNode : oldFlattenedNodes) { - auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( - oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { - // Node was visited - make sure to remove it from - // "newRemainingPairs" map - auto newRemainingIt = - newRemainingPairs.find(oldFlattenedNode.shadowView.tag); - if (newRemainingIt != newRemainingPairs.end()) { - newRemainingPairs.erase(newRemainingIt); - } - } - } - } - - newIndex++; - oldIndex++; - continue; - } - - // Update subtrees if View is not flattened, and if node addresses are - // not equal - if (oldChildPair.shadowNode != newChildPair.shadowNode) { - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode); - calculateShadowViewMutationsV2( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - oldChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } + updateMatchedPair( + DIFF_BREADCRUMB( + "Update Matched Pairs (1): " + + std::to_string(oldChildPair.shadowView.tag)), + mutationContainer, + true, + true, + parentShadowView, + oldChildPair, + newChildPair); + + updateMatchedPairSubtrees( + DIFF_BREADCRUMB( + "Update Matched Pair Subtrees (1): " + + std::to_string(oldChildPair.shadowView.tag)), + scope, + mutationContainer, + newRemainingPairs, + oldChildPairs, + newChildPairs, + parentShadowView, + oldChildPair, + newChildPair); newIndex++; oldIndex++; @@ -1117,7 +1335,7 @@ static void calculateShadowViewMutationsV2( // We have an old pair, but we either don't have any remaining new pairs // or we have one but it's not matched up with the old pair if (haveOldPair) { - auto const &oldChildPair = oldChildPairs[oldIndex]; + auto const &oldChildPair = *oldChildPairs[oldIndex]; Tag oldTag = oldChildPair.shadowView.tag; @@ -1129,148 +1347,29 @@ static void calculateShadowViewMutationsV2( if (insertedIt != newInsertedPairs.end()) { auto const &newChildPair = *insertedIt->second; - // The node has been reordered and we are also flattening or - // unflattening - if (oldChildPair.flattened != newChildPair.flattened) { - DEBUG_LOGS({ - LOG(ERROR) - << "Differ: branch 7: Flattening or unflattening already-inserted node upon remove (move/reorder operation)." - << oldChildPair.shadowView.tag << " " - << oldChildPair.flattened << " // " - << newChildPair.shadowView.tag << " " - << newChildPair.flattened; - }); - - // Unflattening. - // The node in question was already inserted and we are - // *unflattening* it, so we just need to update the subtree nodes - // and remove them from the view hierarchy. Any of the unvisited - // nodes in the old tree will be deleted. - // TODO: can we consolidate this code? It's identical to the first - // block above. - if (!oldChildPair.flattened) { - // Flatten old tree into new list - // At the end of this loop we still want to know which of these - // children are visited, so we reuse the `newRemainingPairs` map. - calculateShadowViewMutationsFlattener( - ReparentMode::Flatten, - mutationInstructionContainer, - parentShadowView, - newRemainingPairs, - oldChildPair); - } - // Unflattening - else { - // Construct unvisited nodes map - auto unvisitedOldChildPairs = - TinyMap{}; - // We don't know where all the children of oldChildPair are within - // oldChildPairs, but we know that they're in the same relative - // order. The reason for this is because of flattening + zIndex: - // the children could be listed before the parent, interwoven with - // children from other nodes, etc. - auto oldFlattenedNodes = sliceChildShadowNodeViewPairsV2( - *oldChildPair.shadowNode, true); - for (size_t i = 0, j = 0; - i < oldChildPairs.size() && j < oldFlattenedNodes.size(); - i++) { - auto &oldChild = oldChildPairs[i]; - if (oldChild.shadowView.tag == - oldFlattenedNodes[j].shadowView.tag) { - unvisitedOldChildPairs.insert( - {oldChild.shadowView.tag, &oldChild}); - j++; - } - } - - // Unflatten old list into new tree - calculateShadowViewMutationsFlattener( - ReparentMode::Unflatten, - mutationInstructionContainer, - parentShadowView, - unvisitedOldChildPairs, - newChildPair); - - // If old nodes were not visited, we know that we can delete them - // now. They will be removed from the hierarchy by the outermost - // loop of this function. TODO: delete recursively? create - // recursively? - for (auto &oldFlattenedNode : oldFlattenedNodes) { - auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( - oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { - // Node was visited - make sure to remove it from - // "newRemainingPairs" map - auto newRemainingIt = - newRemainingPairs.find(oldFlattenedNode.shadowView.tag); - if (newRemainingIt != newRemainingPairs.end()) { - newRemainingPairs.erase(newRemainingIt); - } - } - } - } - } - - // Check concrete-ness of views - // Create/Delete and Insert/Remove if necessary - // TODO: document: Insert should already be handled by outermost loop, - // but not Remove - if (oldChildPair.isConcreteView != newChildPair.isConcreteView) { - if (newChildPair.isConcreteView) { - createMutations.push_back( - ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - } else { - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, - oldChildPair.shadowView, - oldChildPair.mountIndex)); - deleteMutations.push_back( - ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); - } - } - - // We handled this case above. We fall through to check concreteness - // of old/new view to remove/insert create/delete above, and then bail - // out here. - if (oldChildPair.flattened != newChildPair.flattened) { - newInsertedPairs.erase(insertedIt); - oldIndex++; - continue; - } - - // old and new child pairs are both either flattened or unflattened at - // this point. If they're not views, we don't need to update subtrees. - if (oldChildPair.isConcreteView) { - // TODO: do we always want to remove here? There are cases where we - // might be able to remove this to prevent unnecessary - // removes/inserts in cases of (un)flattening + reorders? - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, - oldChildPair.shadowView, - oldChildPair.mountIndex)); - - if (oldChildPair.shadowView != newChildPair.shadowView) { - updateMutations.push_back(ShadowViewMutation::UpdateMutation( - parentShadowView, - oldChildPair.shadowView, - newChildPair.shadowView, - newChildPair.mountIndex)); - } - } - if (!oldChildPair.flattened && - oldChildPair.shadowNode != newChildPair.shadowNode) { - // Update subtrees - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode); - calculateShadowViewMutationsV2( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - oldChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } + updateMatchedPair( + DIFF_BREADCRUMB( + "Update Matched Pairs (2): " + + std::to_string(oldChildPair.shadowView.tag)), + mutationContainer, + true, + false, + parentShadowView, + oldChildPair, + newChildPair); + + updateMatchedPairSubtrees( + DIFF_BREADCRUMB( + "Update Matched Pair Subtrees (2): " + + std::to_string(oldChildPair.shadowView.tag)), + scope, + mutationContainer, + newRemainingPairs, + oldChildPairs, + newChildPairs, + parentShadowView, + oldChildPair, + newChildPair); newInsertedPairs.erase(insertedIt); oldIndex++; @@ -1282,26 +1381,61 @@ static void calculateShadowViewMutationsV2( // generate remove+delete for this node and its subtree. auto const newIt = newRemainingPairs.find(oldTag); if (newIt == newRemainingPairs.end()) { + oldIndex++; + + if (!oldChildPair.isConcreteView) { + continue; + } + + // From here, we know the oldChildPair is concrete. + // We *probably* need to generate a REMOVE mutation (see edge-case + // notes below). + DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 9: Removing tag that was not reinserted: " << oldIndex << ": [" << oldChildPair.shadowView.tag << "]" << (oldChildPair.flattened ? " (flattened)" : "") << (oldChildPair.isConcreteView ? " (concrete)" : "") - << " with parent: [" << parentShadowView.tag << "]"; + << " with parent: [" << parentShadowView.tag << "] " + << "node is in other tree? " + << (oldChildPair.inOtherTree() ? "yes" : "no"); }); - if (oldChildPair.isConcreteView) { - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, - oldChildPair.shadowView, - oldChildPair.mountIndex)); - - deletionCandidatePairs.insert( - {oldChildPair.shadowView.tag, &oldChildPair}); + // Edge case: node is not found in `newRemainingPairs`, due to + // complex (un)flattening cases, but exists in other tree *and* is + // concrete. + if (oldChildPair.inOtherTree() && + oldChildPair.otherTreePair->isConcreteView) { + ShadowView const &otherTreeView = + oldChildPair.otherTreePair->shadowView; + + // Remove, but remove using the *new* node, since we know + // an UPDATE mutation from old -> new has been generated. + // Practically this shouldn't matter for most mounting layer + // implementations, but helps adhere to the invariant that + // for all mutation instructions, "oldViewShadowNode" == "current + // node on mounting layer / stubView". + // Here we do *not" need to generate a potential DELETE mutation + // because we know the view is concrete, and still in the new + // hierarchy. + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + parentShadowView, + otherTreeView, + static_cast(oldChildPair.mountIndex))); + continue; } - oldIndex++; + mutationContainer.removeMutations.push_back( + ShadowViewMutation::RemoveMutation( + parentShadowView, + oldChildPair.shadowView, + static_cast(oldChildPair.mountIndex))); + + deletionCandidatePairs.insert( + {oldChildPair.shadowView.tag, &oldChildPair}); + continue; } } @@ -1309,7 +1443,7 @@ static void calculateShadowViewMutationsV2( // At this point, oldTag is -1 or is in the new list, and hasn't been // inserted or matched yet. We're not sure yet if the new node is in the // old list - generate an insert instruction for the new node. - auto &newChildPair = newChildPairs[newIndex]; + auto &newChildPair = *newChildPairs[newIndex]; DEBUG_LOGS({ LOG(ERROR) << "Differ Branch 10: Inserting tag/tree that was not (yet?) removed from hierarchy: " @@ -1320,21 +1454,29 @@ static void calculateShadowViewMutationsV2( << " with parent: [" << parentShadowView.tag << "]"; }); if (newChildPair.isConcreteView) { - insertMutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, - newChildPair.shadowView, - newChildPair.mountIndex)); + mutationContainer.insertMutations.push_back( + ShadowViewMutation::InsertMutation( + parentShadowView, + newChildPair.shadowView, + static_cast(newChildPair.mountIndex))); } - if (!newChildPair.inOtherTree) { + + // `inOtherTree` is only set to true during flattening/unflattening of + // parent. If the parent isn't (un)flattened, this will always be + // `false`, even if the node is in the other (old) tree. In this case, + // we expect the node to be removed from `newInsertedPairs` when we + // later encounter it in this loop. + if (!newChildPair.inOtherTree()) { newInsertedPairs.insert({newChildPair.shadowView.tag, &newChildPair}); } + newIndex++; } // Penultimate step: generate Delete instructions for entirely deleted // subtrees/nodes. We do this here because we need to traverse the entire - // list to make sure that a node was not reparented into an unflattened node - // that occurs *after* it in the hierarchy, due to zIndex ordering. + // list to make sure that a node was not reparented into an unflattened + // node that occurs *after* it in the hierarchy, due to zIndex ordering. for (auto it = deletionCandidatePairs.begin(); it != deletionCandidatePairs.end(); it++) { @@ -1350,27 +1492,34 @@ static void calculateShadowViewMutationsV2( << "[" << oldChildPair.shadowView.tag << "]" << (oldChildPair.flattened ? "(flattened)" : "") << (oldChildPair.isConcreteView ? "(concrete)" : "") - << (oldChildPair.inOtherTree ? "(in other tree)" : "") - << " with parent: [" << parentShadowView.tag << "]"; + << (oldChildPair.inOtherTree() ? "(in other tree)" : "") + << " with parent: [" << parentShadowView.tag << "] ##" + << std::hash{}(oldChildPair.shadowView); }); // This can happen when the parent is unflattened - if (!oldChildPair.inOtherTree) { - deleteMutations.push_back( + if (!oldChildPair.inOtherTree() && oldChildPair.isConcreteView) { + mutationContainer.deleteMutations.push_back( ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); // We also have to call the algorithm recursively to clean up the // entire subtree starting from the removed view. + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - destructiveDownwardMutations, + DIFF_BREADCRUMB( + "Non-trivial delete " + + std::to_string(oldChildPair.shadowView.tag)), + innerScope, + mutationContainer.destructiveDownwardMutations, oldChildPair.shadowView, - sliceChildShadowNodeViewPairsV2(*oldChildPair.shadowNode), + sliceChildShadowNodeViewPairsFromViewNodePair( + oldChildPair, innerScope), {}); } } - // Final step: generate Create instructions for entirely new subtrees/nodes - // that are not the result of flattening or unflattening. + // Final step: generate Create instructions for entirely new + // subtrees/nodes that are not the result of flattening or unflattening. for (auto it = newInsertedPairs.begin(); it != newInsertedPairs.end(); it++) { // Erased elements of a TinyMap will have a Tag/key of 0 - skip those @@ -1389,61 +1538,70 @@ static void calculateShadowViewMutationsV2( << "[" << newChildPair.shadowView.tag << "]" << (newChildPair.flattened ? "(flattened)" : "") << (newChildPair.isConcreteView ? "(concrete)" : "") - << (newChildPair.inOtherTree ? "(in other tree)" : "") + << (newChildPair.inOtherTree() ? "(in other tree)" : "") << " with parent: [" << parentShadowView.tag << "]"; }); if (!newChildPair.isConcreteView) { continue; } - if (newChildPair.inOtherTree) { + if (newChildPair.inOtherTree()) { continue; } - createMutations.push_back( + mutationContainer.createMutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); + ViewNodePairScope innerScope{}; calculateShadowViewMutationsV2( - downwardMutations, + DIFF_BREADCRUMB( + "Non-trivial create " + + std::to_string(newChildPair.shadowView.tag)), + innerScope, + mutationContainer.downwardMutations, newChildPair.shadowView, {}, - sliceChildShadowNodeViewPairsV2(*newChildPair.shadowNode)); + sliceChildShadowNodeViewPairsFromViewNodePair( + newChildPair, innerScope)); } } // All mutations in an optimal order: std::move( - destructiveDownwardMutations.begin(), - destructiveDownwardMutations.end(), + mutationContainer.destructiveDownwardMutations.begin(), + mutationContainer.destructiveDownwardMutations.end(), std::back_inserter(mutations)); std::move( - updateMutations.begin(), - updateMutations.end(), + mutationContainer.updateMutations.begin(), + mutationContainer.updateMutations.end(), std::back_inserter(mutations)); std::move( - removeMutations.rbegin(), - removeMutations.rend(), + mutationContainer.removeMutations.rbegin(), + mutationContainer.removeMutations.rend(), std::back_inserter(mutations)); std::move( - deleteMutations.begin(), - deleteMutations.end(), + mutationContainer.deleteMutations.begin(), + mutationContainer.deleteMutations.end(), std::back_inserter(mutations)); std::move( - createMutations.begin(), - createMutations.end(), + mutationContainer.createMutations.begin(), + mutationContainer.createMutations.end(), std::back_inserter(mutations)); std::move( - downwardMutations.begin(), - downwardMutations.end(), + mutationContainer.downwardMutations.begin(), + mutationContainer.downwardMutations.end(), std::back_inserter(mutations)); std::move( - insertMutations.begin(), - insertMutations.end(), + mutationContainer.insertMutations.begin(), + mutationContainer.insertMutations.end(), std::back_inserter(mutations)); } -static void sliceChildShadowNodeViewPairsRecursively( - ShadowViewNodePair::List &pairList, +/** + * Only used by unit tests currently. + */ +static void sliceChildShadowNodeViewPairsRecursivelyLegacy( + ShadowViewNodePair::OwningList &pairList, Point layoutOffset, ShadowNode const &shadowNode) { for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { @@ -1473,15 +1631,18 @@ static void sliceChildShadowNodeViewPairsRecursively( pairList.push_back({shadowView, &childShadowNode}); } - sliceChildShadowNodeViewPairsRecursively( + sliceChildShadowNodeViewPairsRecursivelyLegacy( pairList, origin, childShadowNode); } } } -ShadowViewNodePair::List sliceChildShadowNodeViewPairs( +/** + * Only used by unit tests currently. + */ +ShadowViewNodePair::OwningList sliceChildShadowNodeViewPairsLegacy( ShadowNode const &shadowNode) { - auto pairList = ShadowViewNodePair::List{}; + auto pairList = ShadowViewNodePair::OwningList{}; if (!shadowNode.getTraits().check( ShadowNodeTraits::Trait::FormsStackingContext) && @@ -1489,367 +1650,23 @@ ShadowViewNodePair::List sliceChildShadowNodeViewPairs( return pairList; } - sliceChildShadowNodeViewPairsRecursively(pairList, {0, 0}, shadowNode); + sliceChildShadowNodeViewPairsRecursivelyLegacy(pairList, {0, 0}, shadowNode); return pairList; } -static void calculateShadowViewMutations( - ShadowViewMutation::List &mutations, - ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs) { - if (oldChildPairs.empty() && newChildPairs.empty()) { - return; - } - - // Sorting pairs based on `orderIndex` if needed. - reorderInPlaceIfNeeded(oldChildPairs); - reorderInPlaceIfNeeded(newChildPairs); - - auto index = int{0}; - - // Lists of mutations - auto createMutations = ShadowViewMutation::List{}; - auto deleteMutations = ShadowViewMutation::List{}; - auto insertMutations = ShadowViewMutation::List{}; - auto removeMutations = ShadowViewMutation::List{}; - auto updateMutations = ShadowViewMutation::List{}; - auto downwardMutations = ShadowViewMutation::List{}; - auto destructiveDownwardMutations = ShadowViewMutation::List{}; - - // Stage 1: Collecting `Update` mutations - for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size(); - index++) { - auto const &oldChildPair = oldChildPairs[index]; - auto const &newChildPair = newChildPairs[index]; - - if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) { - DEBUG_LOGS({ - LOG(ERROR) << "Differ Branch 1.1: Tags Different: [" - << oldChildPair.shadowView.tag << "] [" - << newChildPair.shadowView.tag << "]"; - }); - - // Totally different nodes, updating is impossible. - break; - } - - DEBUG_LOGS({ - LOG(ERROR) << "Differ Branch 1.2: Same tags, update and recurse: [" - << oldChildPair.shadowView.tag << "]" - << (oldChildPair.flattened ? " (flattened)" : "") - << (oldChildPair.isConcreteView ? " (concrete)" : "") << "[" - << newChildPair.shadowView.tag << "]" - << (newChildPair.flattened ? " (flattened)" : "") - << (newChildPair.isConcreteView ? " (concrete)" : ""); - }); - - if (oldChildPair.shadowView != newChildPair.shadowView) { - updateMutations.push_back(ShadowViewMutation::UpdateMutation( - parentShadowView, - oldChildPair.shadowView, - newChildPair.shadowView, - index)); - } - - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutations( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - oldChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } - - size_t lastIndexAfterFirstStage = index; - - if (index == newChildPairs.size()) { - // We've reached the end of the new children. We can delete+remove the - // rest. - for (; index < oldChildPairs.size(); index++) { - auto const &oldChildPair = oldChildPairs[index]; - - DEBUG_LOGS({ - LOG(ERROR) - << "Differ Branch 2: Deleting Tag/Tree (may be reparented): [" - << oldChildPair.shadowView.tag << "]"; - }); - - deleteMutations.push_back( - ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, oldChildPair.shadowView, index)); - - // We also have to call the algorithm recursively to clean up the entire - // subtree starting from the removed view. - calculateShadowViewMutations( - destructiveDownwardMutations, - oldChildPair.shadowView, - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode), - {}); - } - } else if (index == oldChildPairs.size()) { - // If we don't have any more existing children we can choose a fast path - // since the rest will all be create+insert. - for (; index < newChildPairs.size(); index++) { - auto const &newChildPair = newChildPairs[index]; - - DEBUG_LOGS({ - LOG(ERROR) - << "Differ Branch 3: Creating Tag/Tree (may be reparented): [" - << newChildPair.shadowView.tag << "]"; - }); - - insertMutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, newChildPair.shadowView, index)); - createMutations.push_back( - ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - - calculateShadowViewMutations( - downwardMutations, - newChildPair.shadowView, - {}, - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode)); - } - } else { - // Collect map of tags in the new list - // In the future it would be nice to use TinyMap for newInsertedPairs, but - // it's challenging to build an iterator that will work for our use-case - // here. - auto newRemainingPairs = TinyMap{}; - auto newInsertedPairs = TinyMap{}; - for (; index < newChildPairs.size(); index++) { - auto const &newChildPair = newChildPairs[index]; - newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair}); - } - - // Walk through both lists at the same time - // We will perform updates, create+insert, remove+delete, remove+insert - // (move) here. - size_t oldIndex = lastIndexAfterFirstStage, - newIndex = lastIndexAfterFirstStage, newSize = newChildPairs.size(), - oldSize = oldChildPairs.size(); - while (newIndex < newSize || oldIndex < oldSize) { - bool haveNewPair = newIndex < newSize; - bool haveOldPair = oldIndex < oldSize; - - // Advance both pointers if pointing to the same element - if (haveNewPair && haveOldPair) { - auto const &newChildPair = newChildPairs[newIndex]; - auto const &oldChildPair = oldChildPairs[oldIndex]; - - Tag newTag = newChildPair.shadowView.tag; - Tag oldTag = oldChildPair.shadowView.tag; - - if (newTag == oldTag) { - DEBUG_LOGS({ - LOG(ERROR) << "Differ Branch 5: Matched Tags at indices: " - << oldIndex << " " << newIndex << ": [" - << oldChildPair.shadowView.tag << "][" - << newChildPair.shadowView.tag << "]"; - }); - - // Generate Update instructions - if (oldChildPair.shadowView != newChildPair.shadowView) { - updateMutations.push_back(ShadowViewMutation::UpdateMutation( - parentShadowView, - oldChildPair.shadowView, - newChildPair.shadowView, - index)); - } - - // Remove from newRemainingPairs - auto newRemainingPairIt = newRemainingPairs.find(oldTag); - if (newRemainingPairIt != newRemainingPairs.end()) { - newRemainingPairs.erase(newRemainingPairIt); - } - - // Update subtrees - if (oldChildPair.shadowNode != newChildPair.shadowNode) { - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutations( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - oldChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } - - newIndex++; - oldIndex++; - continue; - } - } - - if (haveOldPair) { - auto const &oldChildPair = oldChildPairs[oldIndex]; - Tag oldTag = oldChildPair.shadowView.tag; - - // Was oldTag already inserted? This indicates a reordering, not just - // a move. The new node has already been inserted, we just need to - // remove the node from its old position now. - auto const insertedIt = newInsertedPairs.find(oldTag); - if (insertedIt != newInsertedPairs.end()) { - DEBUG_LOGS({ - LOG(ERROR) - << "Differ Branch 6: Removing tag that was already inserted: " - << oldIndex << ": [" << oldChildPair.shadowView.tag << "]"; - }); - - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, oldChildPair.shadowView, oldIndex)); - - // Generate update instruction since we have an iterator ref to the - // new node - auto const &newChildPair = *insertedIt->second; - if (oldChildPair.shadowView != newChildPair.shadowView) { - updateMutations.push_back(ShadowViewMutation::UpdateMutation( - parentShadowView, - oldChildPair.shadowView, - newChildPair.shadowView, - index)); - } - - // Update subtrees - if (oldChildPair.shadowNode != newChildPair.shadowNode) { - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutations( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - oldChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } - - newInsertedPairs.erase(insertedIt); - oldIndex++; - continue; - } - - // Should we generate a delete+remove instruction for the old node? - // If there's an old node and it's not found in the "new" list, we - // generate remove+delete for this node and its subtree. - auto const newIt = newRemainingPairs.find(oldTag); - if (newIt == newRemainingPairs.end()) { - DEBUG_LOGS({ - LOG(ERROR) - << "Differ Branch 8: Removing tag/tree that was not reinserted (may be reparented): " - << oldIndex << ": [" << oldChildPair.shadowView.tag << "]"; - }); - - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, oldChildPair.shadowView, oldIndex)); - - deleteMutations.push_back( - ShadowViewMutation::DeleteMutation(oldChildPair.shadowView)); - - // We also have to call the algorithm recursively to clean up the - // entire subtree starting from the removed view. - calculateShadowViewMutations( - destructiveDownwardMutations, - oldChildPair.shadowView, - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode), - {}); - - oldIndex++; - continue; - } - } - - // At this point, oldTag is -1 or is in the new list, and hasn't been - // inserted or matched yet. We're not sure yet if the new node is in the - // old list - generate an insert instruction for the new node. - auto const &newChildPair = newChildPairs[newIndex]; - DEBUG_LOGS({ - LOG(ERROR) - << "Differ Branch 9: Inserting tag/tree that was not yet removed from hierarchy (may be reparented): " - << newIndex << ": [" << newChildPair.shadowView.tag << "]"; - }); - insertMutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, newChildPair.shadowView, newIndex)); - newInsertedPairs.insert({newChildPair.shadowView.tag, &newChildPair}); - newIndex++; - } - - // Final step: generate Create instructions for new nodes - for (auto it = newInsertedPairs.begin(); it != newInsertedPairs.end(); - it++) { - // Erased elements of a TinyMap will have a Tag/key of 0 - skip those - // These *should* be removed by the map; there are currently no KNOWN - // cases where TinyMap will do the wrong thing, but there are not yet - // any unit tests explicitly for TinyMap, so this is safer for now. - if (it->first == 0) { - continue; - } - - auto const &newChildPair = *it->second; - - DEBUG_LOGS({ - LOG(ERROR) - << "Differ Branch 9: Inserting tag/tree that was not yet removed from hierarchy (may be reparented): " - << newIndex << ": [" << newChildPair.shadowView.tag << "]"; - }); - - createMutations.push_back( - ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - - calculateShadowViewMutations( - downwardMutations, - newChildPair.shadowView, - {}, - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode)); - } - } - - // All mutations in an optimal order: - std::move( - destructiveDownwardMutations.begin(), - destructiveDownwardMutations.end(), - std::back_inserter(mutations)); - std::move( - updateMutations.begin(), - updateMutations.end(), - std::back_inserter(mutations)); - std::move( - removeMutations.rbegin(), - removeMutations.rend(), - std::back_inserter(mutations)); - std::move( - deleteMutations.begin(), - deleteMutations.end(), - std::back_inserter(mutations)); - std::move( - createMutations.begin(), - createMutations.end(), - std::back_inserter(mutations)); - std::move( - downwardMutations.begin(), - downwardMutations.end(), - std::back_inserter(mutations)); - std::move( - insertMutations.begin(), - insertMutations.end(), - std::back_inserter(mutations)); -} - ShadowViewMutation::List calculateShadowViewMutations( ShadowNode const &oldRootShadowNode, - ShadowNode const &newRootShadowNode, - bool enableReparentingDetection) { + ShadowNode const &newRootShadowNode) { SystraceSection s("calculateShadowViewMutations"); // Root shadow nodes must be belong the same family. - assert(ShadowNode::sameFamily(oldRootShadowNode, newRootShadowNode)); + react_native_assert( + ShadowNode::sameFamily(oldRootShadowNode, newRootShadowNode)); + + // See explanation of scope in Differentiator.h. + ViewNodePairScope viewNodePairScope{}; + ViewNodePairScope innerViewNodePairScope{}; auto mutations = ShadowViewMutation::List{}; mutations.reserve(256); @@ -1859,22 +1676,16 @@ ShadowViewMutation::List calculateShadowViewMutations( if (oldRootShadowView != newRootShadowView) { mutations.push_back(ShadowViewMutation::UpdateMutation( - ShadowView(), oldRootShadowView, newRootShadowView, -1)); + oldRootShadowView, newRootShadowView)); } - if (enableReparentingDetection) { - calculateShadowViewMutationsV2( - mutations, - ShadowView(oldRootShadowNode), - sliceChildShadowNodeViewPairsV2(oldRootShadowNode), - sliceChildShadowNodeViewPairsV2(newRootShadowNode)); - } else { - calculateShadowViewMutations( - mutations, - ShadowView(oldRootShadowNode), - sliceChildShadowNodeViewPairs(oldRootShadowNode), - sliceChildShadowNodeViewPairs(newRootShadowNode)); - } + calculateShadowViewMutationsV2( + CREATE_DIFF_BREADCRUMB(oldRootShadowView.tag), + innerViewNodePairScope, + mutations, + ShadowView(oldRootShadowNode), + sliceChildShadowNodeViewPairsV2(oldRootShadowNode, viewNodePairScope), + sliceChildShadowNodeViewPairsV2(newRootShadowNode, viewNodePairScope)); return mutations; } diff --git a/android/ReactCommon/react/renderer/mounting/Differentiator.h b/android/ReactCommon/react/renderer/mounting/Differentiator.h index 762bb43ae6bed..551173652c826 100644 --- a/android/ReactCommon/react/renderer/mounting/Differentiator.h +++ b/android/ReactCommon/react/renderer/mounting/Differentiator.h @@ -8,38 +8,64 @@ #pragma once #include +#include #include +#include namespace facebook { namespace react { enum class ReparentMode { Flatten, Unflatten }; +/** + * During differ, we need to keep some `ShadowViewNodePair`s in memory. + * Some `ShadowViewNodePair`s are referenced from std::vectors returned + * by `sliceChildShadowNodeViewPairsV2`; some are referenced in TinyMaps + * for view (un)flattening especially; and it is not always clear which + * std::vectors will outlive which TinyMaps, and vice-versa, so it doesn't + * make sense for the std::vector or TinyMap to own any `ShadowViewNodePair`s. + * + * Thus, we introduce the concept of a scope. + * + * For the duration of some operation, we keep a ViewNodePairScope around, such + * that: (1) the ViewNodePairScope keeps each + * ShadowViewNodePair alive, (2) we have a stable pointer value that we can + * use to reference each ShadowViewNodePair (not guaranteed with std::vector, + * for example, which may have to resize and move values around). + * + * As long as we only manipulate the data-structure with push_back, std::deque + * both (1) ensures that pointers into the data-structure are never invalidated, + * and (2) tries to efficiently allocate storage such that as many objects as + * possible are close in memory, but does not guarantee adjacency. + */ +using ViewNodePairScope = std::deque; + /* * Calculates a list of view mutations which describes how the old * `ShadowTree` can be transformed to the new one. * The list of mutations might be and might not be optimal. */ -ShadowViewMutationList calculateShadowViewMutations( +ShadowViewMutation::List calculateShadowViewMutations( ShadowNode const &oldRootShadowNode, - ShadowNode const &newRootShadowNode, - bool enableReparentingDetection = false); - -/* - * Generates a list of `ShadowViewNodePair`s that represents a layer of a - * flattened view hierarchy. - */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairs( - ShadowNode const &shadowNode); + ShadowNode const &newRootShadowNode); /** * Generates a list of `ShadowViewNodePair`s that represents a layer of a * flattened view hierarchy. The V2 version preserves nodes even if they do * not form views and their children are flattened. */ -ShadowViewNodePair::List sliceChildShadowNodeViewPairsV2( +ShadowViewNodePair::NonOwningList sliceChildShadowNodeViewPairsV2( ShadowNode const &shadowNode, - bool allowFlattened = false); + ViewNodePairScope &viewNodePairScope, + bool allowFlattened = false, + Point layoutOffset = {0, 0}); + +/* + * Generates a list of `ShadowViewNodePair`s that represents a layer of a + * flattened view hierarchy. This is *only* used by unit tests currently. + */ +ShadowViewNodePair::OwningList sliceChildShadowNodeViewPairsLegacy( + ShadowNode const &shadowNode); } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/android/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index 2c9af008a8044..98001cfa1c1f7 100644 --- a/android/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/android/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -14,22 +14,19 @@ #include +#include #include namespace facebook { namespace react { -MountingCoordinator::MountingCoordinator( - ShadowTreeRevision baseRevision, - std::weak_ptr delegate, - bool enableReparentingDetection) +MountingCoordinator::MountingCoordinator(ShadowTreeRevision baseRevision) : surfaceId_(baseRevision.rootShadowNode->getSurfaceId()), baseRevision_(baseRevision), - mountingOverrideDelegate_(delegate), - telemetryController_(*this), - enableReparentingDetection_(enableReparentingDetection) { + telemetryController_(*this) { #ifdef RN_SHADOW_TREE_INTROSPECTION - stubViewTree_ = stubViewTreeFromShadowNode(*baseRevision_.rootShadowNode); + stubViewTree_ = buildStubViewTreeWithoutUsingDifferentiator( + *baseRevision_.rootShadowNode); #endif } @@ -41,7 +38,7 @@ void MountingCoordinator::push(ShadowTreeRevision const &revision) const { { std::lock_guard lock(mutex_); - assert( + react_native_assert( !lastRevision_.has_value() || revision.number != lastRevision_->number); if (!lastRevision_.has_value() || lastRevision_->number < revision.number) { @@ -93,9 +90,7 @@ better::optional MountingCoordinator::pullTransaction() telemetry.willDiff(); auto mutations = calculateShadowViewMutations( - *baseRevision_.rootShadowNode, - *lastRevision_->rootShadowNode, - enableReparentingDetection_); + *baseRevision_.rootShadowNode, *lastRevision_->rootShadowNode); telemetry.didDiff(); @@ -142,8 +137,8 @@ better::optional MountingCoordinator::pullTransaction() // tree therefore we cannot validate the validity of the mutation // instructions. if (!shouldOverridePullTransaction && lastRevision_.has_value()) { - auto stubViewTree = - stubViewTreeFromShadowNode(*lastRevision_->rootShadowNode); + auto stubViewTree = buildStubViewTreeWithoutUsingDifferentiator( + *lastRevision_->rootShadowNode); bool treesEqual = stubViewTree_ == stubViewTree; @@ -168,7 +163,8 @@ better::optional MountingCoordinator::pullTransaction() } } - assert((treesEqual) && "Incorrect set of mutations detected."); + react_native_assert( + (treesEqual) && "Incorrect set of mutations detected."); } } #endif @@ -184,5 +180,11 @@ TelemetryController const &MountingCoordinator::getTelemetryController() const { return telemetryController_; } +void MountingCoordinator::setMountingOverrideDelegate( + std::weak_ptr delegate) const { + std::lock_guard lock(mutex_); + mountingOverrideDelegate_ = delegate; +} + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/MountingCoordinator.h b/android/ReactCommon/react/renderer/mounting/MountingCoordinator.h index 4563dc7bd0aa5..00c12ba06858e 100644 --- a/android/ReactCommon/react/renderer/mounting/MountingCoordinator.h +++ b/android/ReactCommon/react/renderer/mounting/MountingCoordinator.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -17,10 +18,6 @@ #include #include "ShadowTreeRevision.h" -#ifndef NDEBUG -#define RN_SHADOW_TREE_INTROSPECTION 1 -#endif - #ifdef RN_SHADOW_TREE_INTROSPECTION #include #endif @@ -43,10 +40,7 @@ class MountingCoordinator final { * The constructor is meant to be used only inside `ShadowTree`, and it's * `public` only to enable using with `std::make_shared<>`. */ - MountingCoordinator( - ShadowTreeRevision baseRevision, - std::weak_ptr delegate, - bool enableReparentingDetection = false); + MountingCoordinator(ShadowTreeRevision baseRevision); /* * Returns the id of the surface that the coordinator belongs to. @@ -85,6 +79,9 @@ class MountingCoordinator final { void updateBaseRevision(ShadowTreeRevision const &baseRevision) const; void resetLatestRevision() const; + void setMountingOverrideDelegate( + std::weak_ptr delegate) const; + /* * Methods from this section are meant to be used by `ShadowTree` only. */ @@ -110,12 +107,11 @@ class MountingCoordinator final { mutable better::optional lastRevision_{}; mutable MountingTransaction::Number number_{0}; mutable std::condition_variable signal_; - std::weak_ptr mountingOverrideDelegate_; + mutable std::weak_ptr + mountingOverrideDelegate_; TelemetryController telemetryController_; - bool enableReparentingDetection_{false}; // temporary - #ifdef RN_SHADOW_TREE_INTROSPECTION mutable StubViewTree stubViewTree_; // Protected by `mutex_`. #endif diff --git a/android/ReactCommon/react/renderer/mounting/MountingOverrideDelegate.h b/android/ReactCommon/react/renderer/mounting/MountingOverrideDelegate.h index c0c249b8593c8..5aa95d4d80f03 100644 --- a/android/ReactCommon/react/renderer/mounting/MountingOverrideDelegate.h +++ b/android/ReactCommon/react/renderer/mounting/MountingOverrideDelegate.h @@ -22,7 +22,7 @@ class MountingCoordinator; class MountingOverrideDelegate { public: virtual bool shouldOverridePullTransaction() const = 0; - virtual ~MountingOverrideDelegate(){}; + virtual ~MountingOverrideDelegate() = default; /** * Delegates that override this method are responsible for: diff --git a/android/ReactCommon/react/renderer/mounting/MountingTransaction.h b/android/ReactCommon/react/renderer/mounting/MountingTransaction.h index e7545f2c3390a..6f0bbee60fe30 100644 --- a/android/ReactCommon/react/renderer/mounting/MountingTransaction.h +++ b/android/ReactCommon/react/renderer/mounting/MountingTransaction.h @@ -8,8 +8,8 @@ #pragma once #include -#include -#include +#include +#include namespace facebook { namespace react { diff --git a/android/ReactCommon/react/renderer/mounting/MountingTransactionMetadata.h b/android/ReactCommon/react/renderer/mounting/MountingTransactionMetadata.h index 974652bd3297d..176a34c9cda3a 100644 --- a/android/ReactCommon/react/renderer/mounting/MountingTransactionMetadata.h +++ b/android/ReactCommon/react/renderer/mounting/MountingTransactionMetadata.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include namespace facebook { namespace react { diff --git a/android/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/android/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 0cd895fc627b0..1d3c6fe8dce32 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/android/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -7,6 +7,7 @@ #include "ShadowTree.h" +#include #include #include #include @@ -14,7 +15,7 @@ #include #include #include -#include +#include #include "ShadowTreeDelegate.h" @@ -22,6 +23,7 @@ namespace facebook { namespace react { using CommitStatus = ShadowTree::CommitStatus; +using CommitMode = ShadowTree::CommitMode; /* * Generates (possibly) a new tree where all nodes with non-obsolete `State` @@ -176,7 +178,7 @@ static void updateMountedFlag( return; } - int index; + size_t index; // Stage 1: Mount and unmount "updated" children. for (index = 0; index < oldChildren.size() && index < newChildren.size(); @@ -200,7 +202,7 @@ static void updateMountedFlag( updateMountedFlag(oldChild->getChildren(), newChild->getChildren()); } - int lastIndexAfterFirstStage = index; + size_t lastIndexAfterFirstStage = index; // State 2: Mount new children. for (index = lastIndexAfterFirstStage; index < newChildren.size(); index++) { @@ -221,35 +223,39 @@ ShadowTree::ShadowTree( SurfaceId surfaceId, LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext, - RootComponentDescriptor const &rootComponentDescriptor, ShadowTreeDelegate const &delegate, - std::weak_ptr mountingOverrideDelegate, - bool enableReparentingDetection) - : surfaceId_(surfaceId), - delegate_(delegate), - enableReparentingDetection_(enableReparentingDetection) { + ContextContainer const &contextContainer) + : surfaceId_(surfaceId), delegate_(delegate) { const auto noopEventEmitter = std::make_shared( nullptr, -1, std::shared_ptr()); + static auto globalRootComponentDescriptor = + std::make_unique( + ComponentDescriptorParameters{ + EventDispatcher::Shared{}, nullptr, nullptr}); + const auto props = std::make_shared( - *RootShadowNode::defaultSharedProps(), layoutConstraints, layoutContext); + PropsParserContext{surfaceId, contextContainer}, + *RootShadowNode::defaultSharedProps(), + layoutConstraints, + layoutContext); - auto family = rootComponentDescriptor.createFamily( - ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter}, - nullptr); + auto const fragment = + ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter}; + auto family = globalRootComponentDescriptor->createFamily(fragment, nullptr); auto rootShadowNode = std::static_pointer_cast( - rootComponentDescriptor.createShadowNode( + globalRootComponentDescriptor->createShadowNode( ShadowNodeFragment{ /* .props = */ props, }, family)); currentRevision_ = ShadowTreeRevision{ - rootShadowNode, ShadowTreeRevision::Number{0}, TransactionTelemetry{}}; + rootShadowNode, INITIAL_REVISION, TransactionTelemetry{}}; - mountingCoordinator_ = std::make_shared( - currentRevision_, mountingOverrideDelegate, enableReparentingDetection); + mountingCoordinator_ = + std::make_shared(currentRevision_); } ShadowTree::~ShadowTree() { @@ -260,6 +266,31 @@ Tag ShadowTree::getSurfaceId() const { return surfaceId_; } +void ShadowTree::setCommitMode(CommitMode commitMode) const { + auto revision = ShadowTreeRevision{}; + + { + std::unique_lock lock(commitMutex_); + if (commitMode_ == commitMode) { + return; + } + + commitMode_ = commitMode; + revision = currentRevision_; + } + + // initial revision never contains any commits so mounting it here is + // incorrect + if (commitMode == CommitMode::Normal && revision.number != INITIAL_REVISION) { + mount(revision); + } +} + +CommitMode ShadowTree::getCommitMode() const { + std::shared_lock lock(commitMutex_); + return commitMode_; +} + MountingCoordinator::Shared ShadowTree::getMountingCoordinator() const { return mountingCoordinator_; } @@ -281,7 +312,7 @@ CommitStatus ShadowTree::commit( // After multiple attempts, we failed to commit the transaction. // Something internally went terribly wrong. - assert(attempts < 1024); + react_native_assert(attempts < 1024); } } @@ -293,19 +324,22 @@ CommitStatus ShadowTree::tryCommit( auto telemetry = TransactionTelemetry{}; telemetry.willCommit(); + CommitMode commitMode; auto oldRevision = ShadowTreeRevision{}; auto newRevision = ShadowTreeRevision{}; { // Reading `currentRevision_` in shared manner. std::shared_lock lock(commitMutex_); + commitMode = commitMode_; oldRevision = currentRevision_; } + auto oldRootShadowNode = oldRevision.rootShadowNode; auto newRootShadowNode = transaction(*oldRevision.rootShadowNode); if (!newRootShadowNode || - (commitOptions.shouldCancel && commitOptions.shouldCancel())) { + (commitOptions.shouldYield && commitOptions.shouldYield())) { return CommitStatus::Cancelled; } @@ -341,6 +375,14 @@ CommitStatus ShadowTree::tryCommit( auto newRevisionNumber = oldRevision.number + 1; + newRootShadowNode = delegate_.shadowTreeWillCommit( + *this, oldRootShadowNode, newRootShadowNode); + + if (!newRootShadowNode || + (commitOptions.shouldYield && commitOptions.shouldYield())) { + return CommitStatus::Cancelled; + } + { std::lock_guard dispatchLock(EventEmitter::DispatchMutex()); @@ -350,7 +392,7 @@ CommitStatus ShadowTree::tryCommit( } telemetry.didCommit(); - telemetry.setRevisionNumber(newRevisionNumber); + telemetry.setRevisionNumber(static_cast(newRevisionNumber)); newRevision = ShadowTreeRevision{newRootShadowNode, newRevisionNumber, telemetry}; @@ -358,15 +400,11 @@ CommitStatus ShadowTree::tryCommit( currentRevision_ = newRevision; } - if (commitOptions.shouldCancel && commitOptions.shouldCancel()) { - return CommitStatus::Cancelled; - } - emitLayoutEvents(affectedLayoutableNodes); - mountingCoordinator_->push(newRevision); - - notifyDelegatesOfUpdates(); + if (commitMode == CommitMode::Normal) { + mount(newRevision); + } return CommitStatus::Succeeded; } @@ -376,6 +414,11 @@ ShadowTreeRevision ShadowTree::getCurrentRevision() const { return currentRevision_; } +void ShadowTree::mount(ShadowTreeRevision const &revision) const { + mountingCoordinator_->push(revision); + delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_); +} + void ShadowTree::commitEmptyTree() const { commit( [](RootShadowNode const &oldRootShadowNode) -> RootShadowNode::Unshared { diff --git a/android/ReactCommon/react/renderer/mounting/ShadowTree.h b/android/ReactCommon/react/renderer/mounting/ShadowTree.h index 5d208f76aaa70..2553fdc3d310c 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/android/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "MountingOverrideDelegate.h" namespace facebook { @@ -31,17 +32,36 @@ using ShadowTreeCommitTransaction = std::function; + + /* + * Represents a result of a `commit` operation. + */ enum class CommitStatus { Succeeded, Failed, Cancelled, }; + /* + * Represents commits' side-effects propagation mode. + */ + enum class CommitMode { + // Commits' side-effects are observable via `MountingCoordinator`. + // The rendering pipeline fully works end-to-end. + Normal, + + // Commits' side-effects are *not* observable via `MountingCoordinator`. + // The mounting phase is skipped in the rendering pipeline. + Suspended, + }; + struct CommitOptions { bool enableStateReconciliation{false}; - // Lambda called inside `tryCommit`. If false is returned, commit is - // cancelled. - std::function shouldCancel; + + // Called during `tryCommit` phase. Returning true indicates current commit + // should yield to the next commit. + std::function shouldYield; }; /* @@ -51,10 +71,8 @@ class ShadowTree final { SurfaceId surfaceId, LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext, - RootComponentDescriptor const &rootComponentDescriptor, ShadowTreeDelegate const &delegate, - std::weak_ptr mountingOverrideDelegate, - bool enableReparentingDetection = false); + ContextContainer const &contextContainer); ~ShadowTree(); @@ -63,6 +81,14 @@ class ShadowTree final { */ SurfaceId getSurfaceId() const; + /* + * Sets and gets the commit mode. + * Changing commit mode from `Suspended` to `Normal` will flush all suspended + * changes to `MountingCoordinator`. + */ + void setCommitMode(CommitMode commitMode) const; + CommitMode getCommitMode() const; + /* * Performs commit calling `transaction` function with a `oldRootShadowNode` * and expecting a `newRootShadowNode` as a return value. @@ -99,24 +125,21 @@ class ShadowTree final { MountingCoordinator::Shared getMountingCoordinator() const; - /* - * Temporary. - * Do not use. - */ - void setEnableReparentingDetection(bool value) { - enableReparentingDetection_ = value; - } - private: + constexpr static ShadowTreeRevision::Number INITIAL_REVISION{0}; + + void mount(ShadowTreeRevision const &revision) const; + void emitLayoutEvents( std::vector &affectedLayoutableNodes) const; SurfaceId const surfaceId_; ShadowTreeDelegate const &delegate_; mutable better::shared_mutex commitMutex_; + mutable CommitMode commitMode_{ + CommitMode::Normal}; // Protected by `commitMutex_`. mutable ShadowTreeRevision currentRevision_; // Protected by `commitMutex_`. MountingCoordinator::Shared mountingCoordinator_; - bool enableReparentingDetection_{false}; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h b/android/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h index e7e5880e1de9c..45e762c531401 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h +++ b/android/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h @@ -19,6 +19,17 @@ class ShadowTree; */ class ShadowTreeDelegate { public: + /* + * Called right before a ShadowTree commits a new tree. + * The receiver can alter a new (proposed) shadow tree with another tree + * by returning the altered tree. + * Returning a `nullptr` cancels the commit. + */ + virtual RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const = 0; + /* * Called right after Shadow Tree commit a new state of the tree. */ diff --git a/android/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp b/android/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp index 38540ecfd738d..8c801d82270f8 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp +++ b/android/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp @@ -7,11 +7,13 @@ #include "ShadowTreeRegistry.h" +#include + namespace facebook { namespace react { ShadowTreeRegistry::~ShadowTreeRegistry() { - assert( + react_native_assert( registry_.empty() && "Deallocation of non-empty `ShadowTreeRegistry`."); } diff --git a/android/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h b/android/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h index d58dbd643288d..50e05ad7741a5 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h +++ b/android/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace facebook { namespace react { diff --git a/android/ReactCommon/react/renderer/mounting/ShadowView.cpp b/android/ReactCommon/react/renderer/mounting/ShadowView.cpp index f717a28fe669f..3b4b54f1439b6 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowView.cpp +++ b/android/ReactCommon/react/renderer/mounting/ShadowView.cpp @@ -7,6 +7,7 @@ #include "ShadowView.h" +#include #include namespace facebook { @@ -22,7 +23,9 @@ static LayoutMetrics layoutMetricsFromShadowNode(ShadowNode const &shadowNode) { ShadowView::ShadowView(const ShadowNode &shadowNode) : componentName(shadowNode.getComponentName()), componentHandle(shadowNode.getComponentHandle()), + surfaceId(shadowNode.getSurfaceId()), tag(shadowNode.getTag()), + traits(shadowNode.getTraits()), props(shadowNode.getProps()), eventEmitter(shadowNode.getEventEmitter()), layoutMetrics(layoutMetricsFromShadowNode(shadowNode)), @@ -30,6 +33,7 @@ ShadowView::ShadowView(const ShadowNode &shadowNode) bool ShadowView::operator==(const ShadowView &rhs) const { return std::tie( + this->surfaceId, this->tag, this->componentName, this->props, @@ -37,6 +41,7 @@ bool ShadowView::operator==(const ShadowView &rhs) const { this->layoutMetrics, this->state) == std::tie( + rhs.surfaceId, rhs.tag, rhs.componentName, rhs.props, @@ -49,7 +54,7 @@ bool ShadowView::operator!=(const ShadowView &rhs) const { return !(*this == rhs); } -#if RN_DEBUG_STRING_CONVERTIBLE +#ifdef RN_DEBUG_STRING_CONVERTIBLE std::string getDebugName(ShadowView const &object) { return object.componentHandle == 0 ? "Invalid" : object.componentName; @@ -59,7 +64,10 @@ std::vector getDebugProps( ShadowView const &object, DebugStringConvertibleOptions options) { return { + {"surfaceId", getDebugDescription(object.surfaceId, options)}, {"tag", getDebugDescription(object.tag, options)}, + {"traits", getDebugDescription(object.traits, options)}, + {"componentName", object.componentName}, {"props", getDebugDescription(object.props, options)}, {"eventEmitter", getDebugDescription(object.eventEmitter, options)}, {"layoutMetrics", getDebugDescription(object.layoutMetrics, options)}, @@ -77,5 +85,15 @@ bool ShadowViewNodePair::operator!=(const ShadowViewNodePair &rhs) const { return !(*this == rhs); } +bool ShadowViewNodePairLegacy::operator==( + const ShadowViewNodePairLegacy &rhs) const { + return this->shadowNode == rhs.shadowNode; +} + +bool ShadowViewNodePairLegacy::operator!=( + const ShadowViewNodePairLegacy &rhs) const { + return !(*this == rhs); +} + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/ShadowView.h b/android/ReactCommon/react/renderer/mounting/ShadowView.h index 3e65e0262c56f..614399b2fb025 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowView.h +++ b/android/ReactCommon/react/renderer/mounting/ShadowView.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -40,7 +41,9 @@ struct ShadowView final { ComponentName componentName{}; ComponentHandle componentHandle{}; + SurfaceId surfaceId{}; Tag tag{}; + ShadowNodeTraits traits{}; Props::Shared props{}; EventEmitter::Shared eventEmitter{}; LayoutMetrics layoutMetrics{EmptyLayoutMetrics}; @@ -62,23 +65,62 @@ std::vector getDebugProps( * */ struct ShadowViewNodePair final { - using List = better:: + using NonOwningList = better:: + small_vector; + using OwningList = better:: small_vector; ShadowView shadowView; ShadowNode const *shadowNode; bool flattened{false}; bool isConcreteView{true}; + Point contextOrigin{0, 0}; size_t mountIndex{0}; - bool inOtherTree{false}; + /** + * This is nullptr unless `inOtherTree` is set to true. + * We rely on this only for marginal cases. TODO: could we + * rely on this more heavily to simplify the diffing algorithm + * overall? + */ + mutable ShadowViewNodePair const *otherTreePair{nullptr}; /* * The stored pointer to `ShadowNode` represents an identity of the pair. */ bool operator==(const ShadowViewNodePair &rhs) const; bool operator!=(const ShadowViewNodePair &rhs) const; + + bool inOtherTree() const { + return this->otherTreePair != nullptr; + } +}; + +/* + * Describes pair of a `ShadowView` and a `ShadowNode`. + * This is not exposed to the mounting layer. + * + */ +struct ShadowViewNodePairLegacy final { + using OwningList = better::small_vector< + ShadowViewNodePairLegacy, + kShadowNodeChildrenSmallVectorSize>; + + ShadowView shadowView; + ShadowNode const *shadowNode; + bool flattened{false}; + bool isConcreteView{true}; + + size_t mountIndex{0}; + + bool inOtherTree{false}; + + /* + * The stored pointer to `ShadowNode` represents an identity of the pair. + */ + bool operator==(const ShadowViewNodePairLegacy &rhs) const; + bool operator!=(const ShadowViewNodePairLegacy &rhs) const; }; } // namespace react @@ -91,10 +133,12 @@ struct hash { size_t operator()(const facebook::react::ShadowView &shadowView) const { return folly::hash::hash_combine( 0, + shadowView.surfaceId, shadowView.componentHandle, shadowView.tag, shadowView.props, shadowView.eventEmitter, + shadowView.layoutMetrics, shadowView.state); } }; diff --git a/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp b/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp index 9b8d966a3c112..4a77190dff129 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp +++ b/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp @@ -57,19 +57,45 @@ ShadowViewMutation ShadowViewMutation::RemoveMutation( } ShadowViewMutation ShadowViewMutation::UpdateMutation( - ShadowView parentShadowView, ShadowView oldChildShadowView, - ShadowView newChildShadowView, - int index) { + ShadowView newChildShadowView) { return { /* .type = */ Update, - /* .parentShadowView = */ parentShadowView, + /* .parentShadowView = */ {}, /* .oldChildShadowView = */ oldChildShadowView, /* .newChildShadowView = */ newChildShadowView, - /* .index = */ index, + /* .index = */ -1, }; } +bool ShadowViewMutation::mutatedViewIsVirtual() const { + bool viewIsVirtual = false; + +#ifdef ANDROID + // Explanation: Even for non-virtual views, + // for "Insert" mutations, oldChildShadowView is always empty. + // for "Remove" mutations, newChildShadowView is always empty. + // Thus, to see if a view is virtual, we need to always check both the old and + // new View. + viewIsVirtual = newChildShadowView.layoutMetrics == EmptyLayoutMetrics && + oldChildShadowView.layoutMetrics == EmptyLayoutMetrics; +#endif + + return viewIsVirtual; +} + +ShadowViewMutation::ShadowViewMutation( + Type type, + ShadowView parentShadowView, + ShadowView oldChildShadowView, + ShadowView newChildShadowView, + int index) + : type(type), + parentShadowView(parentShadowView), + oldChildShadowView(oldChildShadowView), + newChildShadowView(newChildShadowView), + index(index) {} + #if RN_DEBUG_STRING_CONVERTIBLE std::string getDebugName(ShadowViewMutation const &mutation) { diff --git a/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.h b/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.h index 07ee6bcaf7e48..3ed3414804a54 100644 --- a/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.h +++ b/android/ReactCommon/react/renderer/mounting/ShadowViewMutation.h @@ -23,6 +23,8 @@ namespace react { struct ShadowViewMutation final { using List = std::vector; + ShadowViewMutation() = delete; + #pragma mark - Designated Initializers /* @@ -55,10 +57,8 @@ struct ShadowViewMutation final { * Creates and returns an `Update` mutation. */ static ShadowViewMutation UpdateMutation( - ShadowView parentShadowView, ShadowView oldChildShadowView, - ShadowView newChildShadowView, - int index); + ShadowView newChildShadowView); #pragma mark - Type @@ -70,7 +70,22 @@ struct ShadowViewMutation final { ShadowView parentShadowView = {}; ShadowView oldChildShadowView = {}; ShadowView newChildShadowView = {}; - int index = {}; + int index = -1; + + // Some platforms can have the notion of virtual views - views that are in the + // ShadowTree hierarchy but never are on the platform. Generally this is used + // so notify the platform that a view exists so that we can keep EventEmitters + // around, to notify JS of something. This mechanism is DEPRECATED and it is + // highly recommended that you NOT make use of this in your platform! + bool mutatedViewIsVirtual() const; + + private: + ShadowViewMutation( + Type type, + ShadowView parentShadowView, + ShadowView oldChildShadowView, + ShadowView newChildShadowView, + int index); }; using ShadowViewMutationList = std::vector; diff --git a/android/ReactCommon/react/renderer/mounting/StubView.cpp b/android/ReactCommon/react/renderer/mounting/StubView.cpp index f476de3118489..cad4ae8a39868 100644 --- a/android/ReactCommon/react/renderer/mounting/StubView.cpp +++ b/android/ReactCommon/react/renderer/mounting/StubView.cpp @@ -7,12 +7,30 @@ #include "StubView.h" +#ifdef STUB_VIEW_TREE_VERBOSE +#include +#endif + namespace facebook { namespace react { +StubView::operator ShadowView() const { + auto shadowView = ShadowView{}; + shadowView.componentName = componentName; + shadowView.componentHandle = componentHandle; + shadowView.surfaceId = surfaceId; + shadowView.tag = tag; + shadowView.props = props; + shadowView.eventEmitter = eventEmitter; + shadowView.layoutMetrics = layoutMetrics; + shadowView.state = state; + return shadowView; +} + void StubView::update(ShadowView const &shadowView) { componentName = shadowView.componentName; componentHandle = shadowView.componentHandle; + surfaceId = shadowView.surfaceId; tag = shadowView.tag; props = shadowView.props; eventEmitter = shadowView.eventEmitter; @@ -21,8 +39,23 @@ void StubView::update(ShadowView const &shadowView) { } bool operator==(StubView const &lhs, StubView const &rhs) { - return std::tie(lhs.props, lhs.layoutMetrics) == - std::tie(rhs.props, rhs.layoutMetrics); + if (lhs.props != rhs.props) { +#ifdef STUB_VIEW_TREE_VERBOSE + LOG(ERROR) << "StubView: props do not match. lhs hash: " + << std::hash{}((ShadowView)lhs) + << " rhs hash: " << std::hash{}((ShadowView)rhs); +#endif + return false; + } + if (lhs.layoutMetrics != rhs.layoutMetrics) { +#ifdef STUB_VIEW_TREE_VERBOSE + LOG(ERROR) << "StubView: layoutMetrics do not match lhs hash: " + << std::hash{}((ShadowView)lhs) + << " rhs hash: " << std::hash{}((ShadowView)rhs); +#endif + return false; + } + return true; } bool operator!=(StubView const &lhs, StubView const &rhs) { @@ -33,14 +66,15 @@ bool operator!=(StubView const &lhs, StubView const &rhs) { std::string getDebugName(StubView const &stubView) { return std::string{"Stub"} + - std::string{stubView.componentHandle ? stubView.componentName - : "[invalid]"}; + std::string{ + stubView.componentHandle ? stubView.componentName : "[invalid]"}; } std::vector getDebugProps( StubView const &stubView, DebugStringConvertibleOptions options) { return { + {"surfaceId", getDebugDescription(stubView.surfaceId, options)}, {"tag", getDebugDescription(stubView.tag, options)}, {"props", getDebugDescription(stubView.props, options)}, {"eventEmitter", getDebugDescription(stubView.eventEmitter, options)}, diff --git a/android/ReactCommon/react/renderer/mounting/StubView.h b/android/ReactCommon/react/renderer/mounting/StubView.h index 8546baa157aee..a721c57c74d21 100644 --- a/android/ReactCommon/react/renderer/mounting/StubView.h +++ b/android/ReactCommon/react/renderer/mounting/StubView.h @@ -18,6 +18,8 @@ namespace facebook { namespace react { +static const int NO_VIEW_TAG = -1; + class StubView final { public: using Shared = std::shared_ptr; @@ -25,16 +27,20 @@ class StubView final { StubView() = default; StubView(StubView const &stubView) = default; + operator ShadowView() const; + void update(ShadowView const &shadowView); ComponentName componentName; ComponentHandle componentHandle; + SurfaceId surfaceId; Tag tag; SharedProps props; SharedEventEmitter eventEmitter; LayoutMetrics layoutMetrics; State::Shared state; std::vector children; + Tag parentTag{NO_VIEW_TAG}; }; bool operator==(StubView const &lhs, StubView const &rhs); diff --git a/android/ReactCommon/react/renderer/mounting/StubViewTree.cpp b/android/ReactCommon/react/renderer/mounting/StubViewTree.cpp index 0c9dedee02529..6e82ad5028fb1 100644 --- a/android/ReactCommon/react/renderer/mounting/StubViewTree.cpp +++ b/android/ReactCommon/react/renderer/mounting/StubViewTree.cpp @@ -8,15 +8,7 @@ #include "StubViewTree.h" #include - -// Uncomment to enable verbose StubViewTree debug logs -// #define STUB_VIEW_TREE_VERBOSE 1 - -#define STUB_VIEW_ASSERT(cond) \ - if (!(cond)) { \ - LOG(ERROR) << "ASSERT FAILURE: " << #cond; \ - } \ - assert(cond); +#include #ifdef STUB_VIEW_TREE_VERBOSE #define STUB_VIEW_LOG(code) code @@ -38,127 +30,256 @@ StubView const &StubViewTree::getRootStubView() const { return *registry.at(rootTag); } -/** - * ignoreDuplicateCreates: when stubs generates "fake" mutation instructions, in - * some cases it can produce too many "create" instructions. We ignore - * duplicates and treat them as noops. In the case of verifying actual diffing, - * that assert is left on. - * - * @param mutations - * @param ignoreDuplicateCreates - */ -void StubViewTree::mutate( - ShadowViewMutationList const &mutations, - bool ignoreDuplicateCreates) { +StubView const &StubViewTree::getStubView(Tag tag) const { + return *registry.at(tag); +} + +size_t StubViewTree::size() const { + return registry.size(); +} + +void StubViewTree::mutate(ShadowViewMutationList const &mutations) { STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Mutating Begin"; }); for (auto const &mutation : mutations) { switch (mutation.type) { case ShadowViewMutation::Create: { - STUB_VIEW_ASSERT(mutation.parentShadowView == ShadowView{}); - STUB_VIEW_ASSERT(mutation.oldChildShadowView == ShadowView{}); + react_native_assert(mutation.parentShadowView == ShadowView{}); + react_native_assert(mutation.oldChildShadowView == ShadowView{}); + react_native_assert(mutation.newChildShadowView.props); auto stubView = std::make_shared(); + stubView->update(mutation.newChildShadowView); auto tag = mutation.newChildShadowView.tag; - STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Create: " << tag; }); - if (!ignoreDuplicateCreates) { - STUB_VIEW_ASSERT(registry.find(tag) == registry.end()); - } + STUB_VIEW_LOG({ + LOG(ERROR) << "StubView: Create [" << tag << "] ##" + << std::hash{}((ShadowView)*stubView); + }); + react_native_assert(registry.find(tag) == registry.end()); registry[tag] = stubView; break; } case ShadowViewMutation::Delete: { - STUB_VIEW_LOG( - { LOG(ERROR) << "Delete " << mutation.oldChildShadowView.tag; }); - STUB_VIEW_ASSERT(mutation.parentShadowView == ShadowView{}); - STUB_VIEW_ASSERT(mutation.newChildShadowView == ShadowView{}); + STUB_VIEW_LOG({ + LOG(ERROR) << "StubView: Delete [" << mutation.oldChildShadowView.tag + << "] ##" + << std::hash{}(mutation.oldChildShadowView); + }); + react_native_assert(mutation.parentShadowView == ShadowView{}); + react_native_assert(mutation.newChildShadowView == ShadowView{}); auto tag = mutation.oldChildShadowView.tag; - STUB_VIEW_ASSERT(registry.find(tag) != registry.end()); + react_native_assert(registry.find(tag) != registry.end()); + auto stubView = registry[tag]; + if ((ShadowView)(*stubView) != mutation.oldChildShadowView) { + LOG(ERROR) + << "StubView: ASSERT FAILURE: DELETE mutation assertion failure: oldChildShadowView does not match stubView: [" + << mutation.oldChildShadowView.tag << "] stub hash: ##" + << std::hash{}((ShadowView)*stubView) + << " old mutation hash: ##" + << std::hash{}(mutation.oldChildShadowView); +#ifdef RN_DEBUG_STRING_CONVERTIBLE + LOG(ERROR) << "StubView: " + << getDebugPropsDescription((ShadowView)*stubView, {}); + LOG(ERROR) << "OldChildShadowView: " + << getDebugPropsDescription( + mutation.oldChildShadowView, {}); +#endif + } + react_native_assert( + (ShadowView)(*stubView) == mutation.oldChildShadowView); registry.erase(tag); break; } case ShadowViewMutation::Insert: { - STUB_VIEW_ASSERT(mutation.oldChildShadowView == ShadowView{}); - auto parentTag = mutation.parentShadowView.tag; - STUB_VIEW_ASSERT(registry.find(parentTag) != registry.end()); - auto parentStubView = registry[parentTag]; - auto childTag = mutation.newChildShadowView.tag; - STUB_VIEW_ASSERT(registry.find(childTag) != registry.end()); - auto childStubView = registry[childTag]; - childStubView->update(mutation.newChildShadowView); - STUB_VIEW_LOG({ - LOG(ERROR) << "StubView: Insert: " << childTag << " into " - << parentTag << " at " << mutation.index << "(" - << parentStubView->children.size() << " children)"; - }); - STUB_VIEW_ASSERT(parentStubView->children.size() >= mutation.index); - parentStubView->children.insert( - parentStubView->children.begin() + mutation.index, childStubView); + if (!mutation.mutatedViewIsVirtual()) { + react_native_assert(mutation.oldChildShadowView == ShadowView{}); + auto parentTag = mutation.parentShadowView.tag; + auto childTag = mutation.newChildShadowView.tag; + if (registry.find(parentTag) == registry.end()) { + LOG(ERROR) + << "StubView: ASSERT FAILURE: INSERT mutation assertion failure: parentTag not found: [" + << parentTag << "] inserting child: [" << childTag << "]"; + } + if (registry.find(childTag) == registry.end()) { + LOG(ERROR) + << "StubView: ASSERT FAILURE: INSERT mutation assertion failure: childTag not found: [" + << parentTag << "] inserting child: [" << childTag << "]"; + } + react_native_assert(registry.find(parentTag) != registry.end()); + auto parentStubView = registry[parentTag]; + react_native_assert(registry.find(childTag) != registry.end()); + auto childStubView = registry[childTag]; + childStubView->update(mutation.newChildShadowView); + STUB_VIEW_LOG({ + LOG(ERROR) << "StubView: Insert [" << childTag << "] into [" + << parentTag << "] @" << mutation.index << "(" + << parentStubView->children.size() << " children)"; + }); + react_native_assert(childStubView->parentTag == NO_VIEW_TAG); + react_native_assert( + mutation.index >= 0 && + parentStubView->children.size() >= + static_cast(mutation.index)); + childStubView->parentTag = parentTag; + parentStubView->children.insert( + parentStubView->children.begin() + mutation.index, childStubView); + } else { + auto childTag = mutation.newChildShadowView.tag; + react_native_assert(registry.find(childTag) != registry.end()); + auto childStubView = registry[childTag]; + childStubView->update(mutation.newChildShadowView); + } break; } case ShadowViewMutation::Remove: { - STUB_VIEW_ASSERT(mutation.newChildShadowView == ShadowView{}); - auto parentTag = mutation.parentShadowView.tag; - STUB_VIEW_ASSERT(registry.find(parentTag) != registry.end()); - auto parentStubView = registry[parentTag]; - auto childTag = mutation.oldChildShadowView.tag; - STUB_VIEW_LOG({ - LOG(ERROR) << "StubView: Remove: " << childTag << " from " - << parentTag << " at index " << mutation.index << " with " - << parentStubView->children.size() << " children"; - }); - STUB_VIEW_ASSERT(parentStubView->children.size() > mutation.index); - STUB_VIEW_ASSERT(registry.find(childTag) != registry.end()); - auto childStubView = registry[childTag]; - bool childIsCorrect = - parentStubView->children.size() > mutation.index && - parentStubView->children[mutation.index]->tag == childStubView->tag; - STUB_VIEW_LOG({ - std::string strChildList = ""; - int i = 0; - for (auto const &child : parentStubView->children) { - strChildList.append(std::to_string(i)); - strChildList.append(":"); - strChildList.append(std::to_string(child->tag)); - strChildList.append(", "); - i++; + if (!mutation.mutatedViewIsVirtual()) { + react_native_assert(mutation.newChildShadowView == ShadowView{}); + auto parentTag = mutation.parentShadowView.tag; + auto childTag = mutation.oldChildShadowView.tag; + if (registry.find(parentTag) == registry.end()) { + LOG(ERROR) + << "StubView: ASSERT FAILURE: REMOVE mutation assertion failure: parentTag not found: [" + << parentTag << "] removing child: [" << childTag << "]"; } - LOG(ERROR) << "StubView: BEFORE REMOVE: Children of " << parentTag - << ": " << strChildList; - }); - STUB_VIEW_ASSERT(childIsCorrect); - parentStubView->children.erase( - parentStubView->children.begin() + mutation.index); + react_native_assert(registry.find(parentTag) != registry.end()); + auto parentStubView = registry[parentTag]; + STUB_VIEW_LOG({ + LOG(ERROR) << "StubView: Remove [" << childTag << "] from [" + << parentTag << "] @" << mutation.index << " with " + << parentStubView->children.size() << " children"; + }); + react_native_assert( + mutation.index >= 0 && + parentStubView->children.size() > + static_cast(mutation.index)); + react_native_assert(registry.find(childTag) != registry.end()); + auto childStubView = registry[childTag]; + if ((ShadowView)(*childStubView) != mutation.oldChildShadowView) { + LOG(ERROR) + << "StubView: ASSERT FAILURE: REMOVE mutation assertion failure: oldChildShadowView does not match oldStubView: [" + << mutation.oldChildShadowView.tag << "] stub hash: ##" + << std::hash{}((ShadowView)*childStubView) + << " old mutation hash: ##" + << std::hash{}(mutation.oldChildShadowView); +#ifdef RN_DEBUG_STRING_CONVERTIBLE + LOG(ERROR) << "ChildStubView: " + << getDebugPropsDescription( + (ShadowView)*childStubView, {}); + LOG(ERROR) << "OldChildShadowView: " + << getDebugPropsDescription( + mutation.oldChildShadowView, {}); +#endif + } + react_native_assert( + (ShadowView)(*childStubView) == mutation.oldChildShadowView); + react_native_assert(childStubView->parentTag == parentTag); + STUB_VIEW_LOG({ + std::string strChildList = ""; + int i = 0; + for (auto const &child : parentStubView->children) { + strChildList.append(std::to_string(i)); + strChildList.append(":"); + strChildList.append(std::to_string(child->tag)); + strChildList.append(", "); + i++; + } + LOG(ERROR) << "StubView: BEFORE REMOVE: Children of " << parentTag + << ": " << strChildList; + }); + react_native_assert( + mutation.index >= 0 && + parentStubView->children.size() > + static_cast(mutation.index) && + parentStubView->children[mutation.index]->tag == + childStubView->tag); + childStubView->parentTag = NO_VIEW_TAG; + parentStubView->children.erase( + parentStubView->children.begin() + mutation.index); + } break; } case ShadowViewMutation::Update: { STUB_VIEW_LOG({ - LOG(ERROR) << "StubView: Update: " << mutation.newChildShadowView.tag; + LOG(ERROR) << "StubView: Update [" << mutation.newChildShadowView.tag + << "] old hash: ##" + << std::hash{}(mutation.oldChildShadowView) + << " new hash: ##" + << std::hash{}(mutation.newChildShadowView); }); + react_native_assert(mutation.oldChildShadowView.tag != 0); + react_native_assert(mutation.newChildShadowView.tag != 0); + react_native_assert(mutation.newChildShadowView.props); + react_native_assert( + mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag); + react_native_assert( + registry.find(mutation.newChildShadowView.tag) != registry.end()); + auto oldStubView = registry[mutation.newChildShadowView.tag]; + react_native_assert(oldStubView->tag != 0); + if ((ShadowView)(*oldStubView) != mutation.oldChildShadowView) { + LOG(ERROR) + << "StubView: ASSERT FAILURE: UPDATE mutation assertion failure: oldChildShadowView does not match oldStubView: [" + << mutation.oldChildShadowView.tag << "] old stub hash: ##" + << std::hash{}((ShadowView)*oldStubView) + << " old mutation hash: ##" + << std::hash{}(mutation.oldChildShadowView); +#ifdef RN_DEBUG_STRING_CONVERTIBLE + LOG(ERROR) << "OldStubView: " + << getDebugPropsDescription((ShadowView)*oldStubView, {}); + LOG(ERROR) << "OldChildShadowView: " + << getDebugPropsDescription( + mutation.oldChildShadowView, {}); +#endif + } + react_native_assert( + (ShadowView)(*oldStubView) == mutation.oldChildShadowView); + oldStubView->update(mutation.newChildShadowView); - // We don't have a strict requirement that oldChildShadowView has any - // data. In particular, LayoutAnimations can produce UPDATEs with only a - // new node. - STUB_VIEW_ASSERT( - mutation.newChildShadowView.tag == - mutation.oldChildShadowView.tag || - mutation.oldChildShadowView.tag == 0); + // Hash for stub view and the ShadowView should be identical - this + // tests that StubView and ShadowView hash are equivalent. + react_native_assert( + std::hash{}((ShadowView)*oldStubView) == + std::hash{}(mutation.newChildShadowView)); - STUB_VIEW_ASSERT( - registry.find(mutation.newChildShadowView.tag) != registry.end()); - auto stubView = registry[mutation.newChildShadowView.tag]; - stubView->update(mutation.newChildShadowView); break; } } } STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Mutating End"; }); + + // For iOS especially: flush logs because some might be lost on iOS if an + // assert is hit right after this. + google::FlushLogFiles(google::INFO); } bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) { if (lhs.registry.size() != rhs.registry.size()) { + STUB_VIEW_LOG({ + LOG(ERROR) << "Registry sizes are different. Sizes: LHS: " + << lhs.registry.size() << " RHS: " << rhs.registry.size(); + + [&](std::ostream &stream) -> std::ostream & { + stream << "Tags in LHS: "; + for (auto const &pair : lhs.registry) { + auto &lhsStubView = *lhs.registry.at(pair.first); + stream << "[" << lhsStubView.tag << "]##" + << std::hash{}((ShadowView)lhsStubView) << " "; + } + return stream; + }(LOG(ERROR)); + + [&](std::ostream &stream) -> std::ostream & { + stream << "Tags in RHS: "; + for (auto const &pair : rhs.registry) { + auto &rhsStubView = *rhs.registry.at(pair.first); + stream << "[" << rhsStubView.tag << "]##" + << std::hash{}((ShadowView)rhsStubView) << " "; + } + return stream; + }(LOG(ERROR)); + }); + return false; } @@ -167,6 +288,13 @@ bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) { auto &rhsStubView = *rhs.registry.at(pair.first); if (lhsStubView != rhsStubView) { + STUB_VIEW_LOG({ + LOG(ERROR) << "Registry entries are different. LHS: [" + << lhsStubView.tag << "] ##" + << std::hash{}((ShadowView)lhsStubView) + << " RHS: [" << rhsStubView.tag << "] ##" + << std::hash{}((ShadowView)rhsStubView); + }); return false; } } diff --git a/android/ReactCommon/react/renderer/mounting/StubViewTree.h b/android/ReactCommon/react/renderer/mounting/StubViewTree.h index 3dceaba3dc7e9..c3ce25c8fdf10 100644 --- a/android/ReactCommon/react/renderer/mounting/StubViewTree.h +++ b/android/ReactCommon/react/renderer/mounting/StubViewTree.h @@ -21,14 +21,26 @@ class StubViewTree { StubViewTree() = default; StubViewTree(ShadowView const &shadowView); - void mutate( - ShadowViewMutationList const &mutations, - bool ignoreDuplicateCreates = false); + void mutate(ShadowViewMutationList const &mutations); StubView const &getRootStubView() const; + /* + * Returns a view with given tag. + */ + StubView const &getStubView(Tag tag) const; + + /* + * Returns the total amount of views in the tree. + */ + size_t size() const; + + private: Tag rootTag; std::unordered_map registry{}; + + friend bool operator==(StubViewTree const &lhs, StubViewTree const &rhs); + friend bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs); }; bool operator==(StubViewTree const &lhs, StubViewTree const &rhs); diff --git a/android/ReactCommon/react/renderer/mounting/TelemetryController.cpp b/android/ReactCommon/react/renderer/mounting/TelemetryController.cpp index b058bebcae8ae..a9e36d9a39963 100644 --- a/android/ReactCommon/react/renderer/mounting/TelemetryController.cpp +++ b/android/ReactCommon/react/renderer/mounting/TelemetryController.cpp @@ -30,7 +30,7 @@ bool TelemetryController::pullTransaction( auto surfaceId = transaction.getSurfaceId(); auto number = transaction.getNumber(); auto telemetry = transaction.getTelemetry(); - auto numberOfMutations = transaction.getMutations().size(); + auto numberOfMutations = static_cast(transaction.getMutations().size()); mutex_.lock(); auto compoundTelemetry = compoundTelemetry_; diff --git a/android/ReactCommon/react/renderer/mounting/TelemetryController.h b/android/ReactCommon/react/renderer/mounting/TelemetryController.h index c0b3d28ddd251..3911ea30e4225 100644 --- a/android/ReactCommon/react/renderer/mounting/TelemetryController.h +++ b/android/ReactCommon/react/renderer/mounting/TelemetryController.h @@ -12,7 +12,7 @@ #include #include -#include +#include namespace facebook { namespace react { diff --git a/android/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp b/android/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp deleted file mode 100644 index d81ed7c6bf44b..0000000000000 --- a/android/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "TransactionTelemetry.h" - -#include - -namespace facebook { -namespace react { - -thread_local TransactionTelemetry *threadLocalTransactionTelemetry = nullptr; - -TransactionTelemetry *TransactionTelemetry::threadLocalTelemetry() { - return threadLocalTransactionTelemetry; -} - -void TransactionTelemetry::setAsThreadLocal() { - threadLocalTransactionTelemetry = this; -} - -void TransactionTelemetry::unsetAsThreadLocal() { - threadLocalTransactionTelemetry = nullptr; -} - -void TransactionTelemetry::willCommit() { - assert(commitStartTime_ == kTelemetryUndefinedTimePoint); - assert(commitEndTime_ == kTelemetryUndefinedTimePoint); - commitStartTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::didCommit() { - assert(commitStartTime_ != kTelemetryUndefinedTimePoint); - assert(commitEndTime_ == kTelemetryUndefinedTimePoint); - commitEndTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::willDiff() { - assert(diffStartTime_ == kTelemetryUndefinedTimePoint); - assert(diffEndTime_ == kTelemetryUndefinedTimePoint); - diffStartTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::didDiff() { - assert(diffStartTime_ != kTelemetryUndefinedTimePoint); - assert(diffEndTime_ == kTelemetryUndefinedTimePoint); - diffEndTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::willLayout() { - assert(layoutStartTime_ == kTelemetryUndefinedTimePoint); - assert(layoutEndTime_ == kTelemetryUndefinedTimePoint); - layoutStartTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::didMeasureText() { - numberOfTextMeasurements_++; -} - -void TransactionTelemetry::didLayout() { - assert(layoutStartTime_ != kTelemetryUndefinedTimePoint); - assert(layoutEndTime_ == kTelemetryUndefinedTimePoint); - layoutEndTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::willMount() { - assert(mountStartTime_ == kTelemetryUndefinedTimePoint); - assert(mountEndTime_ == kTelemetryUndefinedTimePoint); - mountStartTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::didMount() { - assert(mountStartTime_ != kTelemetryUndefinedTimePoint); - assert(mountEndTime_ == kTelemetryUndefinedTimePoint); - mountEndTime_ = telemetryTimePointNow(); -} - -void TransactionTelemetry::setRevisionNumber(int revisionNumber) { - revisionNumber_ = revisionNumber; -} - -TelemetryTimePoint TransactionTelemetry::getDiffStartTime() const { - assert(diffStartTime_ != kTelemetryUndefinedTimePoint); - assert(diffEndTime_ != kTelemetryUndefinedTimePoint); - return diffStartTime_; -} - -TelemetryTimePoint TransactionTelemetry::getDiffEndTime() const { - assert(diffStartTime_ != kTelemetryUndefinedTimePoint); - assert(diffEndTime_ != kTelemetryUndefinedTimePoint); - return diffEndTime_; -} - -TelemetryTimePoint TransactionTelemetry::getCommitStartTime() const { - assert(commitStartTime_ != kTelemetryUndefinedTimePoint); - assert(commitEndTime_ != kTelemetryUndefinedTimePoint); - return commitStartTime_; -} - -TelemetryTimePoint TransactionTelemetry::getCommitEndTime() const { - assert(commitStartTime_ != kTelemetryUndefinedTimePoint); - assert(commitEndTime_ != kTelemetryUndefinedTimePoint); - return commitEndTime_; -} - -TelemetryTimePoint TransactionTelemetry::getLayoutStartTime() const { - assert(layoutStartTime_ != kTelemetryUndefinedTimePoint); - assert(layoutEndTime_ != kTelemetryUndefinedTimePoint); - return layoutStartTime_; -} - -TelemetryTimePoint TransactionTelemetry::getLayoutEndTime() const { - assert(layoutStartTime_ != kTelemetryUndefinedTimePoint); - assert(layoutEndTime_ != kTelemetryUndefinedTimePoint); - return layoutEndTime_; -} - -TelemetryTimePoint TransactionTelemetry::getMountStartTime() const { - assert(mountStartTime_ != kTelemetryUndefinedTimePoint); - assert(mountEndTime_ != kTelemetryUndefinedTimePoint); - return mountStartTime_; -} - -TelemetryTimePoint TransactionTelemetry::getMountEndTime() const { - assert(mountStartTime_ != kTelemetryUndefinedTimePoint); - assert(mountEndTime_ != kTelemetryUndefinedTimePoint); - return mountEndTime_; -} - -int TransactionTelemetry::getNumberOfTextMeasurements() const { - return numberOfTextMeasurements_; -} - -int TransactionTelemetry::getRevisionNumber() const { - return revisionNumber_; -} - -} // namespace react -} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/stubs.cpp b/android/ReactCommon/react/renderer/mounting/stubs.cpp index 47ce20e9e2c40..c5f76beb203e0 100644 --- a/android/ReactCommon/react/renderer/mounting/stubs.cpp +++ b/android/ReactCommon/react/renderer/mounting/stubs.cpp @@ -14,6 +14,26 @@ namespace facebook { namespace react { +/* + * Sorting comparator for `reorderInPlaceIfNeeded`. + */ +static bool shouldFirstPairComesBeforeSecondOne( + ShadowViewNodePair const &lhs, + ShadowViewNodePair const &rhs) noexcept { + return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex(); +} + +/* + * Reorders pairs in-place based on `orderIndex` using a stable sort algorithm. + */ +static void reorderInPlaceIfNeeded( + ShadowViewNodePair::OwningList &pairs) noexcept { + // This is a simplified version of the function intentionally copied from + // `Differentiator.cpp`. + std::stable_sort( + pairs.begin(), pairs.end(), &shouldFirstPairComesBeforeSecondOne); +} + /* * Generates `create` and `insert` instructions recursively traversing a shadow * tree. @@ -23,38 +43,56 @@ namespace react { static void calculateShadowViewMutationsForNewTree( ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, - ShadowViewNodePair::List const &newChildPairs) { - for (auto index = 0; index < newChildPairs.size(); index++) { + ShadowViewNodePair::OwningList newChildPairs) { + // Sorting pairs based on `orderIndex` if needed. + reorderInPlaceIfNeeded(newChildPairs); + + for (size_t index = 0; index < newChildPairs.size(); index++) { auto const &newChildPair = newChildPairs[index]; mutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); mutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, newChildPair.shadowView, index)); + parentShadowView, newChildPair.shadowView, static_cast(index))); - auto const newGrandChildPairs = - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); + auto newGrandChildPairs = + sliceChildShadowNodeViewPairsLegacy(*newChildPair.shadowNode); calculateShadowViewMutationsForNewTree( mutations, newChildPair.shadowView, newGrandChildPairs); } } -StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode) { +StubViewTree buildStubViewTreeWithoutUsingDifferentiator( + ShadowNode const &rootShadowNode) { auto mutations = ShadowViewMutation::List{}; mutations.reserve(256); calculateShadowViewMutationsForNewTree( mutations, ShadowView(rootShadowNode), - sliceChildShadowNodeViewPairs(rootShadowNode)); + sliceChildShadowNodeViewPairsLegacy(rootShadowNode)); + + auto emptyRootShadowNode = rootShadowNode.clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + ShadowNode::emptySharedShadowNodeSharedList()}); + + auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode)); + stubViewTree.mutate(mutations); + return stubViewTree; +} + +StubViewTree buildStubViewTreeUsingDifferentiator( + ShadowNode const &rootShadowNode) { + auto emptyRootShadowNode = rootShadowNode.clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + ShadowNode::emptySharedShadowNodeSharedList()}); - auto emptyRootShadowNode = rootShadowNode.clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - ShadowNode::emptySharedShadowNodeSharedList()}); + auto mutations = + calculateShadowViewMutations(*emptyRootShadowNode, rootShadowNode); auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode)); - stubViewTree.mutate(mutations, true); + stubViewTree.mutate(mutations); return stubViewTree; } diff --git a/android/ReactCommon/react/renderer/mounting/stubs.h b/android/ReactCommon/react/renderer/mounting/stubs.h index 8a7b564c08107..73ac7b1d8eccc 100644 --- a/android/ReactCommon/react/renderer/mounting/stubs.h +++ b/android/ReactCommon/react/renderer/mounting/stubs.h @@ -14,7 +14,19 @@ namespace facebook { namespace react { -StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode); +/* + * Builds a ShadowView tree from given root ShadowNode using custom built-in + * implementation (*without* using Differentiator). + */ +StubViewTree buildStubViewTreeWithoutUsingDifferentiator( + ShadowNode const &rootShadowNode); + +/* + * Builds a ShadowView tree from given root ShadowNode using Differentiator by + * generating mutation instructions between empty and final trees. + */ +StubViewTree buildStubViewTreeUsingDifferentiator( + ShadowNode const &rootShadowNode); } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/tests/MountingTest.cpp b/android/ReactCommon/react/renderer/mounting/tests/MountingTest.cpp index 172134fa39c5e..8ad2395bef768 100644 --- a/android/ReactCommon/react/renderer/mounting/tests/MountingTest.cpp +++ b/android/ReactCommon/react/renderer/mounting/tests/MountingTest.cpp @@ -9,10 +9,11 @@ #include #include +#include #include #include -#include "shadowTreeGeneration.h" +#include #include #include @@ -31,8 +32,12 @@ static SharedViewProps nonFlattenedDefaultProps( dynamic["nativeId"] = "NativeId"; dynamic["accessible"] = true; + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + return std::static_pointer_cast( - componentDescriptor.cloneProps(nullptr, RawProps{dynamic})); + componentDescriptor.cloneProps( + parserContext, nullptr, RawProps{dynamic})); } static ShadowNode::Shared makeNode( @@ -44,8 +49,8 @@ static ShadowNode::Shared makeNode( : nonFlattenedDefaultProps(componentDescriptor); return componentDescriptor.createShadowNode( - ShadowNodeFragment{props, - std::make_shared(children)}, + ShadowNodeFragment{ + props, std::make_shared(children)}, componentDescriptor.createFamily({tag, SurfaceId(1), nullptr}, nullptr)); } @@ -78,10 +83,13 @@ TEST(MountingTest, testReorderingInstructionGeneration) { ShadowNodeFragment{RootShadowNode::defaultSharedProps()}, rootFamily))); + PropsParserContext parserContext{-1, *contextContainer}; + // Applying size constraints. emptyRootNode = emptyRootNode->clone( - LayoutConstraints{Size{512, 0}, - Size{512, std::numeric_limits::infinity()}}, + parserContext, + LayoutConstraints{ + Size{512, 0}, Size{512, std::numeric_limits::infinity()}}, LayoutContext{}); auto childA = makeNode(viewComponentDescriptor, 100, {}); @@ -101,22 +109,23 @@ TEST(MountingTest, testReorderingInstructionGeneration) { // Construct "identical" shadow nodes: they differ only in children. auto shadowNodeV1 = viewComponentDescriptor.createShadowNode( - ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor), - std::make_shared( - SharedShadowNodeList{childB, childC, childD})}, + ShadowNodeFragment{ + generateDefaultProps(viewComponentDescriptor), + std::make_shared( + SharedShadowNodeList{childB, childC, childD})}, family); auto shadowNodeV2 = shadowNodeV1->clone(ShadowNodeFragment{ generateDefaultProps(viewComponentDescriptor), std::make_shared( SharedShadowNodeList{childA, childB, childC, childD})}); - auto shadowNodeV3 = shadowNodeV2->clone( - ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor), - std::make_shared( - SharedShadowNodeList{childB, childC, childD})}); - auto shadowNodeV4 = shadowNodeV3->clone( - ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor), - std::make_shared( - SharedShadowNodeList{childB, childD, childE})}); + auto shadowNodeV3 = shadowNodeV2->clone(ShadowNodeFragment{ + generateDefaultProps(viewComponentDescriptor), + std::make_shared( + SharedShadowNodeList{childB, childC, childD})}); + auto shadowNodeV4 = shadowNodeV3->clone(ShadowNodeFragment{ + generateDefaultProps(viewComponentDescriptor), + std::make_shared( + SharedShadowNodeList{childB, childD, childE})}); auto shadowNodeV5 = shadowNodeV4->clone(ShadowNodeFragment{ generateDefaultProps(viewComponentDescriptor), std::make_shared( @@ -127,52 +136,53 @@ TEST(MountingTest, testReorderingInstructionGeneration) { childB, childA, childD, childF, childE, childC})}); auto shadowNodeV7 = shadowNodeV6->clone(ShadowNodeFragment{ generateDefaultProps(viewComponentDescriptor), - std::make_shared(SharedShadowNodeList{childF, - childE, - childC, - childD, - childG, - childH, - childI, - childJ, - childK})}); + std::make_shared(SharedShadowNodeList{ + childF, + childE, + childC, + childD, + childG, + childH, + childI, + childJ, + childK})}); // Injecting a tree into the root node. auto rootNodeV1 = std::static_pointer_cast( - emptyRootNode->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV1})})); + emptyRootNode->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV1})})); auto rootNodeV2 = std::static_pointer_cast( - rootNodeV1->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV2})})); + rootNodeV1->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV2})})); auto rootNodeV3 = std::static_pointer_cast( - rootNodeV2->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV3})})); + rootNodeV2->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV3})})); auto rootNodeV4 = std::static_pointer_cast( - rootNodeV3->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV4})})); + rootNodeV3->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV4})})); auto rootNodeV5 = std::static_pointer_cast( - rootNodeV4->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV5})})); + rootNodeV4->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV5})})); auto rootNodeV6 = std::static_pointer_cast( - rootNodeV5->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV6})})); + rootNodeV5->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV6})})); auto rootNodeV7 = std::static_pointer_cast( - rootNodeV6->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV7})})); + rootNodeV6->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV7})})); // Layout std::vector affectedLayoutableNodesV1{}; @@ -240,8 +250,7 @@ TEST(MountingTest, testReorderingInstructionGeneration) { }*/ // Calculating mutations. - auto mutations1 = - calculateShadowViewMutations(*rootNodeV1, *rootNodeV2, false); + auto mutations1 = calculateShadowViewMutations(*rootNodeV1, *rootNodeV2); // The order and exact mutation instructions here may change at any time. // This test just ensures that any changes are intentional. @@ -257,8 +266,7 @@ TEST(MountingTest, testReorderingInstructionGeneration) { EXPECT_TRUE(mutations1[1].index == 0); // Calculating mutations. - auto mutations2 = - calculateShadowViewMutations(*rootNodeV2, *rootNodeV3, false); + auto mutations2 = calculateShadowViewMutations(*rootNodeV2, *rootNodeV3); // The order and exact mutation instructions here may change at any time. // This test just ensures that any changes are intentional. @@ -274,8 +282,7 @@ TEST(MountingTest, testReorderingInstructionGeneration) { EXPECT_TRUE(mutations2[1].oldChildShadowView.tag == 100); // Calculating mutations. - auto mutations3 = - calculateShadowViewMutations(*rootNodeV3, *rootNodeV4, false); + auto mutations3 = calculateShadowViewMutations(*rootNodeV3, *rootNodeV4); LOG(ERROR) << "Num mutations IN OLD TEST mutations3: " << mutations3.size(); // The order and exact mutation instructions here may change at any time. @@ -297,8 +304,7 @@ TEST(MountingTest, testReorderingInstructionGeneration) { EXPECT_TRUE(mutations3[3].index == 2); // Calculating mutations. - auto mutations4 = - calculateShadowViewMutations(*rootNodeV4, *rootNodeV5, false); + auto mutations4 = calculateShadowViewMutations(*rootNodeV4, *rootNodeV5); // The order and exact mutation instructions here may change at any time. // This test just ensures that any changes are intentional. @@ -323,8 +329,7 @@ TEST(MountingTest, testReorderingInstructionGeneration) { EXPECT_TRUE(mutations4[5].newChildShadowView.tag == 102); EXPECT_TRUE(mutations4[5].index == 3); - auto mutations5 = - calculateShadowViewMutations(*rootNodeV5, *rootNodeV6, false); + auto mutations5 = calculateShadowViewMutations(*rootNodeV5, *rootNodeV6); // The order and exact mutation instructions here may change at any time. // This test just ensures that any changes are intentional. @@ -343,8 +348,7 @@ TEST(MountingTest, testReorderingInstructionGeneration) { EXPECT_TRUE(mutations5[3].newChildShadowView.tag == 105); EXPECT_TRUE(mutations5[3].index == 3); - auto mutations6 = - calculateShadowViewMutations(*rootNodeV6, *rootNodeV7, false); + auto mutations6 = calculateShadowViewMutations(*rootNodeV6, *rootNodeV7); // The order and exact mutation instructions here may change at any time. // This test just ensures that any changes are intentional. @@ -387,10 +391,13 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) { ShadowNodeFragment{RootShadowNode::defaultSharedProps()}, rootFamily))); + PropsParserContext parserContext{-1, *contextContainer}; + // Applying size constraints. emptyRootNode = emptyRootNode->clone( - LayoutConstraints{Size{512, 0}, - Size{512, std::numeric_limits::infinity()}}, + parserContext, + LayoutConstraints{ + Size{512, 0}, Size{512, std::numeric_limits::infinity()}}, LayoutContext{}); auto childA = makeNode(viewComponentDescriptor, 100, {}); @@ -541,30 +548,30 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) { // Injecting a tree into the root node. auto rootNodeV1 = std::static_pointer_cast( - emptyRootNode->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV1})})); + emptyRootNode->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV1})})); auto rootNodeV2 = std::static_pointer_cast( - rootNodeV1->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV2})})); + rootNodeV1->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV2})})); auto rootNodeV3 = std::static_pointer_cast( - rootNodeV2->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV3})})); + rootNodeV2->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV3})})); auto rootNodeV4 = std::static_pointer_cast( - rootNodeV3->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV4})})); + rootNodeV3->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV4})})); auto rootNodeV5 = std::static_pointer_cast( - rootNodeV4->ShadowNode::clone( - ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(), - std::make_shared( - SharedShadowNodeList{shadowNodeV5})})); + rootNodeV4->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{shadowNodeV5})})); // Layout std::vector affectedLayoutableNodesV1{}; @@ -598,8 +605,7 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) { rootNodeV5->sealRecursive(); // Calculating mutations. - auto mutations1 = - calculateShadowViewMutations(*rootNodeV1, *rootNodeV2, true); + auto mutations1 = calculateShadowViewMutations(*rootNodeV1, *rootNodeV2); EXPECT_EQ(mutations1.size(), 5); EXPECT_EQ(mutations1[0].type, ShadowViewMutation::Update); @@ -613,8 +619,7 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) { EXPECT_EQ(mutations1[4].type, ShadowViewMutation::Insert); EXPECT_EQ(mutations1[4].newChildShadowView.tag, 1000); - auto mutations2 = - calculateShadowViewMutations(*rootNodeV2, *rootNodeV3, true); + auto mutations2 = calculateShadowViewMutations(*rootNodeV2, *rootNodeV3); EXPECT_EQ(mutations2.size(), 5); EXPECT_EQ(mutations2[0].type, ShadowViewMutation::Update); @@ -630,8 +635,7 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) { EXPECT_EQ(mutations2[4].type, ShadowViewMutation::Insert); EXPECT_EQ(mutations2[4].newChildShadowView.tag, 1000); - auto mutations3 = - calculateShadowViewMutations(*rootNodeV3, *rootNodeV4, true); + auto mutations3 = calculateShadowViewMutations(*rootNodeV3, *rootNodeV4); // between these two trees, lots of new nodes are created and inserted - this // is all correct, and this is the minimal amount of mutations @@ -668,8 +672,7 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) { EXPECT_EQ(mutations3[14].type, ShadowViewMutation::Insert); EXPECT_EQ(mutations3[14].newChildShadowView.tag, 1000); - auto mutations4 = - calculateShadowViewMutations(*rootNodeV4, *rootNodeV5, true); + auto mutations4 = calculateShadowViewMutations(*rootNodeV4, *rootNodeV5); EXPECT_EQ(mutations4.size(), 9); EXPECT_EQ(mutations4[0].type, ShadowViewMutation::Update); diff --git a/android/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp b/android/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp index 1ecfd21a69b67..da9806e58dc5c 100644 --- a/android/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp +++ b/android/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp @@ -12,12 +12,17 @@ #include #include +#include #include #include + #include +#include +#include -#include "Entropy.h" -#include "shadowTreeGeneration.h" +// Uncomment when random test blocks are uncommented below. +// #include +// #include namespace facebook { namespace react { @@ -26,8 +31,7 @@ static void testShadowNodeTreeLifeCycle( uint_fast32_t seed, int treeSize, int repeats, - int stages, - bool useFlattener) { + int stages) { auto entropy = seed == 0 ? Entropy() : Entropy(seed); auto eventDispatcher = EventDispatcher::Shared{}; @@ -41,6 +45,8 @@ static void testShadowNodeTreeLifeCycle( auto noopEventEmitter = std::make_shared(nullptr, -1, eventDispatcher); + PropsParserContext parserContext{-1, *contextContainer}; + auto allNodes = std::vector{}; for (int i = 0; i < repeats; i++) { @@ -58,8 +64,9 @@ static void testShadowNodeTreeLifeCycle( // Applying size constraints. emptyRootNode = emptyRootNode->clone( - LayoutConstraints{Size{512, 0}, - Size{512, std::numeric_limits::infinity()}}, + parserContext, + LayoutConstraints{ + Size{512, 0}, Size{512, std::numeric_limits::infinity()}}, LayoutContext{}); // Generation of a random tree. @@ -74,7 +81,7 @@ static void testShadowNodeTreeLifeCycle( SharedShadowNodeList{singleRootChildNode})})); // Building an initial view hierarchy. - auto viewTree = stubViewTreeFromShadowNode(*emptyRootNode); + auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode); viewTree.mutate( calculateShadowViewMutations(*emptyRootNode, *currentRootNode)); @@ -88,7 +95,7 @@ static void testShadowNodeTreeLifeCycle( { &messWithChildren, &messWithYogaStyles, - &messWithLayotableOnlyFlag, + &messWithLayoutableOnlyFlag, }); std::vector affectedLayoutableNodes{}; @@ -102,12 +109,12 @@ static void testShadowNodeTreeLifeCycle( allNodes.push_back(nextRootNode); // Calculating mutations. - auto mutations = calculateShadowViewMutations( - *currentRootNode, *nextRootNode, useFlattener); + auto mutations = + calculateShadowViewMutations(*currentRootNode, *nextRootNode); - // If using flattener: make sure that in a single frame, a DELETE for a + // Make sure that in a single frame, a DELETE for a // view is not followed by a CREATE for the same view. - if (useFlattener) { + { std::vector deletedTags{}; for (auto const &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Delete) { @@ -122,7 +129,7 @@ static void testShadowNodeTreeLifeCycle( mutation.newChildShadowView.tag) != deletedTags.end()) { LOG(ERROR) << "Deleted tag was recreated in mutations list: [" << mutation.newChildShadowView.tag << "]"; - FAIL(); + react_native_assert(false); } } } @@ -132,7 +139,8 @@ static void testShadowNodeTreeLifeCycle( viewTree.mutate(mutations); // Building a view tree to compare with. - auto rebuiltViewTree = stubViewTreeFromShadowNode(*nextRootNode); + auto rebuiltViewTree = + buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode); // Comparing the newly built tree with the updated one. if (rebuiltViewTree != viewTree) { @@ -142,7 +150,7 @@ static void testShadowNodeTreeLifeCycle( // There are some issues getting `getDebugDescription` to compile // under test on Android for now. -#ifndef ANDROID +#ifdef RN_DEBUG_STRING_CONVERTIBLE LOG(ERROR) << "Shadow Tree before: \n" << currentRootNode->getDebugDescription(); LOG(ERROR) << "Shadow Tree after: \n" @@ -159,7 +167,158 @@ static void testShadowNodeTreeLifeCycle( << getDebugDescription(mutations, {}); #endif - FAIL(); + react_native_assert(false); + } + + currentRootNode = nextRootNode; + } + } + + SUCCEED(); +} + +static void testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening( + uint_fast32_t seed, + int treeSize, + int repeats, + int stages) { + auto entropy = seed == 0 ? Entropy() : Entropy(seed); + + auto eventDispatcher = EventDispatcher::Shared{}; + auto contextContainer = std::make_shared(); + auto componentDescriptorParameters = + ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr}; + auto viewComponentDescriptor = + ViewComponentDescriptor(componentDescriptorParameters); + auto rootComponentDescriptor = + RootComponentDescriptor(componentDescriptorParameters); + auto noopEventEmitter = + std::make_shared(nullptr, -1, eventDispatcher); + + PropsParserContext parserContext{-1, *contextContainer}; + + auto allNodes = std::vector{}; + + for (int i = 0; i < repeats; i++) { + allNodes.clear(); + + auto family = rootComponentDescriptor.createFamily( + {Tag(1), SurfaceId(1), nullptr}, nullptr); + + // Creating an initial root shadow node. + auto emptyRootNode = std::const_pointer_cast( + std::static_pointer_cast( + rootComponentDescriptor.createShadowNode( + ShadowNodeFragment{RootShadowNode::defaultSharedProps()}, + family))); + + // Applying size constraints. + emptyRootNode = emptyRootNode->clone( + parserContext, + LayoutConstraints{ + Size{512, 0}, Size{512, std::numeric_limits::infinity()}}, + LayoutContext{}); + + // Generation of a random tree. + auto singleRootChildNode = + generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize); + + // Injecting a tree into the root node. + auto currentRootNode = std::static_pointer_cast( + emptyRootNode->ShadowNode::clone(ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + std::make_shared( + SharedShadowNodeList{singleRootChildNode})})); + + // Building an initial view hierarchy. + auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode); + viewTree.mutate( + calculateShadowViewMutations(*emptyRootNode, *currentRootNode)); + + for (int j = 0; j < stages; j++) { + auto nextRootNode = currentRootNode; + + // Mutating the tree. + alterShadowTree( + entropy, + nextRootNode, + { + &messWithYogaStyles, + &messWithLayoutableOnlyFlag, + }); + alterShadowTree(entropy, nextRootNode, &messWithNodeFlattenednessFlags); + alterShadowTree(entropy, nextRootNode, &messWithChildren); + + std::vector affectedLayoutableNodes{}; + affectedLayoutableNodes.reserve(1024); + + // Laying out the tree. + std::const_pointer_cast(nextRootNode) + ->layoutIfNeeded(&affectedLayoutableNodes); + + nextRootNode->sealRecursive(); + allNodes.push_back(nextRootNode); + + // Calculating mutations. + auto mutations = + calculateShadowViewMutations(*currentRootNode, *nextRootNode); + + // Make sure that in a single frame, a DELETE for a + // view is not followed by a CREATE for the same view. + { + std::vector deletedTags{}; + for (auto const &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Delete) { + deletedTags.push_back(mutation.oldChildShadowView.tag); + } + } + for (auto const &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Create) { + if (std::find( + deletedTags.begin(), + deletedTags.end(), + mutation.newChildShadowView.tag) != deletedTags.end()) { + LOG(ERROR) << "Deleted tag was recreated in mutations list: [" + << mutation.newChildShadowView.tag << "]"; + react_native_assert(false); + } + } + } + } + + // Mutating the view tree. + viewTree.mutate(mutations); + + // Building a view tree to compare with. + auto rebuiltViewTree = + buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode); + + // Comparing the newly built tree with the updated one. + if (rebuiltViewTree != viewTree) { + // Something went wrong. + + LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n"; + + // There are some issues getting `getDebugDescription` to compile + // under test on Android for now. +#ifdef RN_DEBUG_STRING_CONVERTIBLE + LOG(ERROR) << "Shadow Tree before: \n" + << currentRootNode->getDebugDescription(); + LOG(ERROR) << "Shadow Tree after: \n" + << nextRootNode->getDebugDescription(); + + LOG(ERROR) << "View Tree before: \n" + << getDebugDescription(viewTree.getRootStubView(), {}); + LOG(ERROR) << "View Tree after: \n" + << getDebugDescription( + rebuiltViewTree.getRootStubView(), {}); + + LOG(ERROR) << "Mutations:" + << "\n" + << getDebugDescription(mutations, {}); +#endif + + react_native_assert(false); } currentRootNode = nextRootNode; @@ -174,47 +333,90 @@ static void testShadowNodeTreeLifeCycle( using namespace facebook::react; -TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMoves) { +TEST( + ShadowTreeLifecyleTest, + stableBiggerTreeFewerIterationsOptimizedMovesFlattener) { testShadowNodeTreeLifeCycle( /* seed */ 0, /* size */ 512, /* repeats */ 32, - /* stages */ 32, - false); + /* stages */ 32); } -TEST(MountingTest, stableSmallerTreeMoreIterationsOptimizedMoves) { +TEST( + ShadowTreeLifecyleTest, + stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) { + testShadowNodeTreeLifeCycle( + /* seed */ 1, + /* size */ 512, + /* repeats */ 32, + /* stages */ 32); +} + +TEST( + ShadowTreeLifecyleTest, + stableSmallerTreeMoreIterationsOptimizedMovesFlattener) { testShadowNodeTreeLifeCycle( /* seed */ 0, /* size */ 16, /* repeats */ 512, - /* stages */ 32, - false); + /* stages */ 32); } -TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMovesFlattener) { - testShadowNodeTreeLifeCycle( - /* seed */ 0, - /* size */ 512, +TEST( + ShadowTreeLifecyleTest, + unstableSmallerTreeFewerIterationsExtensiveFlatteningUnflattening) { + testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening( + /* seed */ 1337, + /* size */ 32, /* repeats */ 32, - /* stages */ 32, - true); + /* stages */ 32); } -TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) { - testShadowNodeTreeLifeCycle( - /* seed */ 1, - /* size */ 512, +TEST( + ShadowTreeLifecyleTest, + unstableBiggerTreeFewerIterationsExtensiveFlatteningUnflattening) { + testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening( + /* seed */ 1337, + /* size */ 256, /* repeats */ 32, - /* stages */ 32, - true); + /* stages */ 32); } -TEST(MountingTest, stableSmallerTreeMoreIterationsOptimizedMovesFlattener) { - testShadowNodeTreeLifeCycle( - /* seed */ 0, - /* size */ 16, +TEST( + ShadowTreeLifecyleTest, + unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening) { + testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening( + /* seed */ 1337, + /* size */ 32, /* repeats */ 512, - /* stages */ 32, - true); + /* stages */ 32); } + +// failing test case found 4-25-2021 +TEST( + ShadowTreeLifecyleTest, + unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening_1167342011) { + testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening( + /* seed */ 1167342011, + /* size */ 32, + /* repeats */ 512, + /* stages */ 32); +} + +// You may uncomment this - locally only! - to generate failing seeds. +// TEST( +// ShadowTreeLifecyleTest, +// unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflatteningManyRandom) +// { +// std::random_device device; +// for (int i = 0; i < 10; i++) { +// uint_fast32_t seed = device(); +// LOG(ERROR) << "Seed: " << seed; +// testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening( +// /* seed */ seed, +// /* size */ 32, +// /* repeats */ 512, +// /* stages */ 32); +// } +// } diff --git a/android/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp b/android/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp new file mode 100644 index 0000000000000..521d3135d1311 --- /dev/null +++ b/android/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp @@ -0,0 +1,787 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class StackingContextTest : public ::testing::Test { + protected: + ComponentBuilder builder_; + std::shared_ptr rootShadowNode_; + std::shared_ptr nodeA_; + std::shared_ptr nodeAA_; + std::shared_ptr nodeB_; + std::shared_ptr nodeBA_; + std::shared_ptr nodeBB_; + std::shared_ptr nodeBBA_; + std::shared_ptr nodeBBB_; + std::shared_ptr nodeBC_; + std::shared_ptr nodeBD_; + + std::shared_ptr currentRootShadowNode_; + StubViewTree currentStubViewTree_; + + StackingContextTest() : builder_(simpleComponentBuilder()) { + // ┌────────────── (Root) ──────────────┐ + // │ ┏━ A (tag: 2) ━━━━━━━━━━━━━━━━━━━┓ │ + // │ ┃ ┃ │ + // │ ┃ ┃ │ + // │ ┃ ┃ │ + // │ ┃ ┃ │ + // │ ┃ ┏━ AA (tag: 3) ━━━━━━━━━━━━━━┓ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┏━ B (tag: 4) ━━━━━━━━━━━━━━━━━━━┓ │ + // │ ┃ ┃ │ + // │ ┃ ┃ │ + // │ ┃ ┃ │ + // │ ┃ ┃ │ + // │ ┃ ┏━ BA (tag: 5) ━━━━━━━━━━━━━━┓ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ ┃ ┏━ BB (tag: 6) ━━━━━━━━━━━━━━┓ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┏━ BBA (tag: 7) ━━━━━━━━━┓ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ + // │ ┃ ┃ ┏━ BBB (tag: 8) ━━━━━━━━━┓ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ ┃ ┏━ BC (tag: 9) ━━━━━━━━━━━━━━┓ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ ┃ ┏━ BD (tag: 10) ━━━━━━━━━━━━━┓ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // └────────────────────────────────────┘ + + // clang-format off + auto element = + Element() + .reference(rootShadowNode_) + .tag(1) + .children({ + Element() + .tag(2) + .reference(nodeA_) + .children({ + Element() + .tag(3) + .reference(nodeAA_) + }), + Element() + .tag(4) + .reference(nodeB_) + .children({ + Element() + .tag(5) + .reference(nodeBA_), + Element() + .tag(6) + .reference(nodeBB_) + .children({ + Element() + .tag(7) + .reference(nodeBBA_), + Element() + .tag(8) + .reference(nodeBBB_) + }), + Element() + .tag(9) + .reference(nodeBC_), + Element() + .tag(10) + .reference(nodeBD_) + }) + }); + // clang-format on + + builder_.build(element); + + currentRootShadowNode_ = rootShadowNode_; + currentRootShadowNode_->layoutIfNeeded(); + currentStubViewTree_ = + buildStubViewTreeWithoutUsingDifferentiator(*currentRootShadowNode_); + } + + void mutateViewShadowNodeProps_( + std::shared_ptr node, + std::function callback) { + rootShadowNode_ = + std::static_pointer_cast(rootShadowNode_->cloneTree( + node->getFamily(), [&](ShadowNode const &oldShadowNode) { + auto viewProps = std::make_shared(); + callback(*viewProps); + return oldShadowNode.clone(ShadowNodeFragment{viewProps}); + })); + } + + void testViewTree_( + std::function callback) { + rootShadowNode_->layoutIfNeeded(); + + callback(buildStubViewTreeUsingDifferentiator(*rootShadowNode_)); + callback(buildStubViewTreeWithoutUsingDifferentiator(*rootShadowNode_)); + + auto mutations = + calculateShadowViewMutations(*currentRootShadowNode_, *rootShadowNode_); + currentRootShadowNode_ = rootShadowNode_; + currentStubViewTree_.mutate(mutations); + callback(currentStubViewTree_); + } +}; + +TEST_F(StackingContextTest, defaultPropsMakeEverythingFlattened) { + testViewTree_([](StubViewTree const &viewTree) { + // 1 view in total. + EXPECT_EQ(viewTree.size(), 1); + + // The root view has no subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 0); + }); +} + +TEST_F(StackingContextTest, mostPropsDoNotForceViewsToMaterialize) { + // ┌────────────── (Root) ──────────────┐ ┌────────── (Root) ───────────┐ + // │ ┏━ A (tag: 2) ━━━━━━━━━━━━━━━━━━━┓ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┏━ AA (tag: 3) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ padding: 10; ┃ ┃ │ │ │ + // │ ┃ ┃ margin: 9001; ┃ ┃ │ │ │ + // │ ┃ ┃ position: absolute; ┃ ┃ │ │ │ + // │ ┃ ┃ shadowRadius: 10; ┃ ┃ │ │ │ + // │ ┃ ┃ shadowOffset: [42, 42]; ┃ ┃ │ │ │ + // │ ┃ ┃ backgroundColor: clear; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ │ + // │ ┏━ B (tag: 4) ━━━━━━━━━━━━━━━━━━━┓ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┏━ BA (tag: 5) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ zIndex: 42; ┃ ┃ │ │ │ + // │ ┃ ┃ margin: 42; ┃ ┃ │ │ │ + // │ ┃ ┃ shadowColor: clear; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BB (tag: 6) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ No observable side-effects. │ + // │ ┃ ┃ ┃ ┃ │━━━▶│ No views are generated. │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┏━ BBA (tag: 7) ━━━━━━━━━┓ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ position: relative; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ borderRadii: 42; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ borderColor: black; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ │ + // │ ┃ ┃ ┏━ BBB (tag: 8) ━━━━━━━━━┓ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BC (tag: 9) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BD (tag: 10) ━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ onLayout: true; ┃ ┃ │ │ │ + // │ ┃ ┃ hitSlop: 42; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ │ + // └────────────────────────────────────┘ └─────────────────────────────┘ + + mutateViewShadowNodeProps_(nodeAA_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.padding()[YGEdgeAll] = YGValue{42, YGUnitPoint}; + yogaStyle.margin()[YGEdgeAll] = YGValue{42, YGUnitPoint}; + yogaStyle.positionType() = YGPositionTypeAbsolute; + props.shadowRadius = 42; + props.shadowOffset = Size{42, 42}; + props.backgroundColor = clearColor(); + }); + + mutateViewShadowNodeProps_(nodeBA_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + props.zIndex = 42; + yogaStyle.margin()[YGEdgeAll] = YGValue{42, YGUnitPoint}; + props.shadowColor = clearColor(); + props.shadowOpacity = 0.42; + }); + + mutateViewShadowNodeProps_(nodeBBA_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + + props.borderRadii.all = 42; + props.borderColors.all = blackColor(); + }); + + mutateViewShadowNodeProps_(nodeBD_, [](ViewProps &props) { + props.onLayout = true; + props.hitSlop = EdgeInsets{42, 42, 42, 42}; + }); + + testViewTree_([](StubViewTree const &viewTree) { + // 1 view in total. + EXPECT_EQ(viewTree.size(), 1); + + // The root view has no subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 0); + }); +} + +TEST_F(StackingContextTest, somePropsForceViewsToMaterialize1) { + // ┌────────────── (Root) ──────────────┐ ┌─────────── (Root) ──────────┐ + // │ ┏━ A (tag: 2) ━━━━━━━━━━━━━━━━━━━┓ │ │ ┏━ AA (tag: 3) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┏━ AA (tag: 3) ━━━━━━━━━━━━━━┓ ┃ │ │ ┏━ BA (tag: 5) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ backgroundColor: black; ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ │ │ ┏━ BBA (tag: 7) ━━━━━━━━━━┓ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ ┃ ┃ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┏━ B (tag: 4) ━━━━━━━━━━━━━━━━━━━┓ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┃ │ │ │ + // │ ┃ ┏━ BA (tag: 5) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ backgroundColor: white; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BB (tag: 6) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │━━━▶│ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┏━ BBA (tag: 7) ━━━━━━━━━┓ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ shadowColor: black; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ │ + // │ ┃ ┃ ┏━ BBB (tag: 8) ━━━━━━━━━┓ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BC (tag: 9) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BD (tag: 10) ━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ │ + // └────────────────────────────────────┘ └─────────────────────────────┘ + + mutateViewShadowNodeProps_( + nodeAA_, [](ViewProps &props) { props.backgroundColor = blackColor(); }); + + mutateViewShadowNodeProps_( + nodeBA_, [](ViewProps &props) { props.backgroundColor = whiteColor(); }); + + mutateViewShadowNodeProps_( + nodeBBA_, [](ViewProps &props) { props.shadowColor = blackColor(); }); + + testViewTree_([](StubViewTree const &viewTree) { + // 4 views in total. + EXPECT_EQ(viewTree.size(), 4); + + // The root view has all 3 subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 3); + + // The root view subviews are [3, 5, 7]. + EXPECT_EQ(viewTree.getRootStubView().children.at(0)->tag, 3); + EXPECT_EQ(viewTree.getRootStubView().children.at(1)->tag, 5); + EXPECT_EQ(viewTree.getRootStubView().children.at(2)->tag, 7); + }); +} + +TEST_F(StackingContextTest, somePropsForceViewsToMaterialize2) { + // ┌────────────── (Root) ──────────────┐ ┌─────────── (Root) ──────────┐ + // │ ┏━ A (tag: 2) ━━━━━━━━━━━━━━━━━━━┓ │ │ ┏━ A (tag: 2) ━━━━━━━━━━━━┓ │ + // │ ┃ backgroundColor: black; ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┏━ AA (tag: 3) ━━━━━━━━━━━━━━┓ ┃ │ │ ┏━ AA (tag: 3) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ pointerEvents: none; ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ │ │ ┏━ B (tag: 4) ━━━━━━━━━━━━┓ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ ┃ ┃ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┏━ B (tag: 4) ━━━━━━━━━━━━━━━━━━━┓ │ │ ┏━ BA (tag: 5) ━━━━━━━━━━━┓ │ + // │ ┃ testId: "42" ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┏━ BA (tag: 5) ━━━━━━━━━━━━━━┓ ┃ │ │ ┏━ BB (tag: 6) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ nativeId: "42" ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ ┏━ BBA (tag: 7) ━━━━━━━━━━┓ │ + // │ ┃ ┏━ BB (tag: 6) ━━━━━━━━━━━━━━┓ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ foregroundColor: black; ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ ┃ │━━━▶│ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ │ │ ┏━ BBB (tag: 8) ━━━━━━━━━━┓ │ + // │ ┃ ┃ ┏━ BBA (tag: 7) ━━━━━━━━━┓ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ transform: scale(2); ┃ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ ┏━ BC (tag: 9) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┏━ BBB (tag: 8) ━━━━━━━━━┓ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ position: relative; ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ zIndex: 42; ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ ┏━ BD (tag: 10) ━━━━━━━━━━┓ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ ┃ ┃ │ + // │ ┃ ┏━ BC (tag: 9) ━━━━━━━━━━━━━━┓ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ shadowColor: black; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BD (tag: 10) ━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ opacity: 0.42; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ │ + // └────────────────────────────────────┘ └─────────────────────────────┘ + + mutateViewShadowNodeProps_( + nodeA_, [](ViewProps &props) { props.backgroundColor = blackColor(); }); + + mutateViewShadowNodeProps_(nodeAA_, [](ViewProps &props) { + props.pointerEvents = PointerEventsMode::None; + }); + + mutateViewShadowNodeProps_( + nodeB_, [](ViewProps &props) { props.testId = "42"; }); + + mutateViewShadowNodeProps_( + nodeBA_, [](ViewProps &props) { props.nativeId = "42"; }); + + mutateViewShadowNodeProps_( + nodeBB_, [](ViewProps &props) { props.foregroundColor = blackColor(); }); + + mutateViewShadowNodeProps_(nodeBBA_, [](ViewProps &props) { + props.transform = Transform::Scale(2, 2, 2); + }); + + mutateViewShadowNodeProps_(nodeBBB_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 42; + }); + + mutateViewShadowNodeProps_( + nodeBC_, [](ViewProps &props) { props.shadowColor = blackColor(); }); + + mutateViewShadowNodeProps_( + nodeBD_, [](ViewProps &props) { props.opacity = 0.42; }); + + testViewTree_([](StubViewTree const &viewTree) { + // 10 views in total. + EXPECT_EQ(viewTree.size(), 10); + + // The root view has all 9 subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 9); + }); +} + +TEST_F(StackingContextTest, zIndexAndFlattenedNodes) { + // ┌────────────── (Root) ──────────────┐ ┌────────── (Root) ───────────┐ + // │ ┏━ A (tag: 2) ━━━━━━━━━━━━━━━━━━━┓ │ │ ┏━ BD (tag: 10) ━━━━━━━━━━┓ │ + // │ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┏━ AA (tag: 3) ━━━━━━━━━━━━━━┓ ┃ │ │ ┏━ BC (tag: 9) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ position: relative; ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ zIndex: 9001; ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ │ │ ┏━ BBB (tag: 8) ━━━━━━━━━━┓ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ ┃ ┃ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┏━ B (tag: 4) ━━━━━━━━━━━━━━━━━━━┓ │ │ ┏━ BBA (tag: 7) ━━━━━━━━━━┓ │ + // │ ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┏━ BA (tag: 5) ━━━━━━━━━━━━━━┓ ┃ │ │ ┏━ BA (tag: 5) ━━━━━━━━━━━┓ │ + // │ ┃ ┃ position: relative; ┃ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ zIndex: 9000; ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ ┏━ AA (tag: 3) ━━━━━━━━━━━┓ │ + // │ ┃ ┏━ BB (tag: 6) ━━━━━━━━━━━━━━┓ ┃ │ │ ┃ #FormsView ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┃ #FormsStackingContext ┃ │ + // │ ┃ ┃ ┃ ┃ │━━━▶│ ┃ ┃ │ + // │ ┃ ┃ ┃ ┃ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┏━ BBA (tag: 7) ━━━━━━━━━┓ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ position: relative; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ zIndex: 8999; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ │ + // │ ┃ ┃ ┏━ BBB (tag: 8) ━━━━━━━━━┓ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ position: relative; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ zIndex: 8998; ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BC (tag: 9) ━━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ position: relative; ┃ ┃ │ │ │ + // │ ┃ ┃ zIndex: 8997; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┃ ┏━ BD (tag: 10) ━━━━━━━━━━━━━┓ ┃ │ │ │ + // │ ┃ ┃ position: relative; ┃ ┃ │ │ │ + // │ ┃ ┃ zIndex: 8996; ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┃ ┃ ┃ │ │ │ + // │ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ │ │ │ + // │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ │ + // └────────────────────────────────────┘ └─────────────────────────────┘ + + mutateViewShadowNodeProps_(nodeAA_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 9001; + }); + + mutateViewShadowNodeProps_(nodeBA_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 9000; + }); + + mutateViewShadowNodeProps_(nodeBBA_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 8999; + }); + + mutateViewShadowNodeProps_(nodeBBB_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 8998; + }); + + mutateViewShadowNodeProps_(nodeBC_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 8997; + }); + + mutateViewShadowNodeProps_(nodeBD_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 8996; + }); + + testViewTree_([](StubViewTree const &viewTree) { + // 7 views in total. + EXPECT_EQ(viewTree.size(), 7); + + // The root view has all 6 subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 6); + + // The root view subviews are [10, 9, 8, 7, 5, 3]. + EXPECT_EQ(viewTree.getRootStubView().children.at(0)->tag, 10); + EXPECT_EQ(viewTree.getRootStubView().children.at(1)->tag, 9); + EXPECT_EQ(viewTree.getRootStubView().children.at(2)->tag, 8); + EXPECT_EQ(viewTree.getRootStubView().children.at(3)->tag, 7); + EXPECT_EQ(viewTree.getRootStubView().children.at(4)->tag, 5); + EXPECT_EQ(viewTree.getRootStubView().children.at(5)->tag, 3); + }); + + // And now let's make BB to form a Stacking Context with small order-index. + + // ┌────────────── (Root) ──────────────┐ ┌────────── (Root) ──────────┐ + // │ ┌─ A (tag: 2) ───────────────────┐ │ │ ┏━ BB (tag: 6) ━━━━━━━━━━┓ │ + // │ │ │ │ │ ┃ #View ┃ │ + // │ │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ │ │ │ ┃ ┃ │ + // │ │ │ │ │ ┃ ┏━ BBB (tag: 8) ━━━━━┓ ┃ │ + // │ │ ┌─ AA (tag: 3) ──────────────┐ │ │ │ ┃ ┃ #View ┃ ┃ │ + // │ │ │ position: relative; │ │ │ │ ┃ ┃ #StackingContext ┃ ┃ │ + // │ │ │ zIndex: 9001; │ │ │ │ ┃ ┃ ┃ ┃ │ + // │ │ │ │ │ │ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ │ │ │ │ │ │ ┃ ┏━ BBA (tag: 7) ━━━━━┓ ┃ │ + // │ │ │ │ │ │ │ ┃ ┃ #View ┃ ┃ │ + // │ │ │ │ │ │ │ ┃ ┃ #StackingContext ┃ ┃ │ + // │ │ │ │ │ │ │ ┃ ┃ ┃ ┃ │ + // │ │ └────────────────────────────┘ │ │ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━┛ ┃ │ + // │ └────────────────────────────────┘ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┌─ B (tag: 4) ───────────────────┐ │ │ ┏━ BD (tag: 10) ━━━━━━━━━┓ │ + // │ │ │ │ │ ┃ #View ┃ │ + // │ │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ │ │ │ ┃ ┃ │ + // │ │ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ ┌─ BA (tag: 5) ──────────────┐ │ │ │ ┏━ BC (tag: 9) ━━━━━━━━━━┓ │ + // │ │ │ position: relative; │ │ │ │ ┃ #View ┃ │ + // │ │ │ zIndex: 9000; │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ │ │ │ │ │ ┃ ┃ │ + // │ │ │ │ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ └────────────────────────────┘ │ │ │ ┏━ BA (tag: 5) ━━━━━━━━━━┓ │ + // │ │ ╔═ BB (tag: 6) ══════════════╗ │ │ │ ┃ #View ┃ │ + // │ │ ║ *** position: relative; ║ │ │ │ ┃ #StackingContext ┃ │ + // │ │ ║ *** zIndex: 42; ║ │ │━━━━▶│ ┃ ┃ │ + // │ │ ║ ║ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ ║ ║ │ │ │ ┏━ AA (tag: 3) ━━━━━━━━━━┓ │ + // │ │ ║ ┌─ BBA (tag: 7) ─────────┐ ║ │ │ │ ┃ #View ┃ │ + // │ │ ║ │ position: relative; │ ║ │ │ │ ┃ #StackingContext ┃ │ + // │ │ ║ │ zIndex: 8999; │ ║ │ │ │ ┃ ┃ │ + // │ │ ║ │ │ ║ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ └────────────────────────┘ ║ │ │ │ │ + // │ │ ║ ┌─ BBB (tag: 8) ─────────┐ ║ │ │ │ │ + // │ │ ║ │ position: relative; │ ║ │ │ │ │ + // │ │ ║ │ zIndex: 8998; │ ║ │ │ │ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ └────────────────────────┘ ║ │ │ │ │ + // │ │ ╚════════════════════════════╝ │ │ │ │ + // │ │ ┌─ BC (tag: 9) ──────────────┐ │ │ │ │ + // │ │ │ position: relative; │ │ │ │ │ + // │ │ │ zIndex: 8997; │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ └────────────────────────────┘ │ │ │ │ + // │ │ ┌─ BD (tag: 10) ─────────────┐ │ │ │ │ + // │ │ │ position: relative; │ │ │ │ │ + // │ │ │ zIndex: 8996; │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ └────────────────────────────┘ │ │ │ │ + // │ └────────────────────────────────┘ │ │ │ + // └────────────────────────────────────┘ └────────────────────────────┘ + + mutateViewShadowNodeProps_(nodeBB_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeRelative; + props.zIndex = 42; + }); + + testViewTree_([](StubViewTree const &viewTree) { + // 8 views in total. + EXPECT_EQ(viewTree.size(), 8); + + // The root view has 5 subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 5); + + // The root view subviews are [6, 10, 9, 5, 3]. + EXPECT_EQ(viewTree.getRootStubView().children.at(0)->tag, 6); + EXPECT_EQ(viewTree.getRootStubView().children.at(1)->tag, 10); + EXPECT_EQ(viewTree.getRootStubView().children.at(2)->tag, 9); + EXPECT_EQ(viewTree.getRootStubView().children.at(3)->tag, 5); + EXPECT_EQ(viewTree.getRootStubView().children.at(4)->tag, 3); + + auto &view6 = viewTree.getStubView(6); + EXPECT_EQ(view6.children.size(), 2); + EXPECT_EQ(view6.children.at(0)->tag, 8); + EXPECT_EQ(view6.children.at(1)->tag, 7); + }); + + // And now, let's revert it back. + + mutateViewShadowNodeProps_(nodeBB_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.positionType() = YGPositionTypeStatic; + props.zIndex = {}; + }); + + testViewTree_([](StubViewTree const &viewTree) { + // 7 views in total. + EXPECT_EQ(viewTree.size(), 7); + + // The root view has all 6 subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 6); + + // The root view subviews are [10, 9, 8, 7, 5, 3]. + EXPECT_EQ(viewTree.getRootStubView().children.at(0)->tag, 10); + EXPECT_EQ(viewTree.getRootStubView().children.at(1)->tag, 9); + EXPECT_EQ(viewTree.getRootStubView().children.at(2)->tag, 8); + EXPECT_EQ(viewTree.getRootStubView().children.at(3)->tag, 7); + EXPECT_EQ(viewTree.getRootStubView().children.at(4)->tag, 5); + EXPECT_EQ(viewTree.getRootStubView().children.at(5)->tag, 3); + }); + + // And now, let's hide BB completety. + + // ┌────────────── (Root) ──────────────┐ ┌────────── (Root) ──────────┐ + // │ ┌─ A (tag: 2) ───────────────────┐ │ │ ┏━ BD (tag: 10) ━━━━━━━━━┓ │ + // │ │ │ │ │ ┃ #View ┃ │ + // │ │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ │ │ │ ┃ ┃ │ + // │ │ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ ┌─ AA (tag: 3) ──────────────┐ │ │ │ ┏━ BC (tag: 9) ━━━━━━━━━━┓ │ + // │ │ │ position: relative; │ │ │ │ ┃ #View ┃ │ + // │ │ │ zIndex: 9001; │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ │ │ │ │ │ ┃ ┃ │ + // │ │ │ │ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ │ │ │ │ │ ┏━ BA (tag: 5) ━━━━━━━━━━┓ │ + // │ │ │ │ │ │ │ ┃ #View ┃ │ + // │ │ │ │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ └────────────────────────────┘ │ │ │ ┃ ┃ │ + // │ └────────────────────────────────┘ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ ┌─ B (tag: 4) ───────────────────┐ │ │ ┏━ AA (tag: 3) ━━━━━━━━━━┓ │ + // │ │ │ │ │ ┃ #View ┃ │ + // │ │ │ │ │ ┃ #StackingContext ┃ │ + // │ │ │ │ │ ┃ ┃ │ + // │ │ │ │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━┛ │ + // │ │ ┌─ BA (tag: 5) ──────────────┐ │ │ │ │ + // │ │ │ position: relative; │ │ │ │ │ + // │ │ │ zIndex: 9000; │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ └────────────────────────────┘ │ │ │ │ + // │ │ ╔═ BB (tag: 6) ══════════════╗ │ │ │ │ + // │ │ ║ *** display: none; ║ │ │ │ │ + // │ │ ║ ║ │ │━━━━▶│ │ + // │ │ ║ ║ │ │ │ │ + // │ │ ║ ║ │ │ │ │ + // │ │ ║ ┌─ BBA (tag: 7) ─────────┐ ║ │ │ │ │ + // │ │ ║ │ position: relative; │ ║ │ │ │ │ + // │ │ ║ │ zIndex: 8999; │ ║ │ │ │ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ └────────────────────────┘ ║ │ │ │ │ + // │ │ ║ ┌─ BBB (tag: 8) ─────────┐ ║ │ │ │ │ + // │ │ ║ │ position: relative; │ ║ │ │ │ │ + // │ │ ║ │ zIndex: 8998; │ ║ │ │ │ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ │ │ ║ │ │ │ │ + // │ │ ║ └────────────────────────┘ ║ │ │ │ │ + // │ │ ╚════════════════════════════╝ │ │ │ │ + // │ │ ┌─ BC (tag: 9) ──────────────┐ │ │ │ │ + // │ │ │ position: relative; │ │ │ │ │ + // │ │ │ zIndex: 8997; │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ └────────────────────────────┘ │ │ │ │ + // │ │ ┌─ BD (tag: 10) ─────────────┐ │ │ │ │ + // │ │ │ position: relative; │ │ │ │ │ + // │ │ │ zIndex: 8996; │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ │ │ │ │ │ │ + // │ │ └────────────────────────────┘ │ │ │ │ + // │ └────────────────────────────────┘ │ │ │ + // └────────────────────────────────────┘ └────────────────────────────┘ + + mutateViewShadowNodeProps_(nodeBB_, [](ViewProps &props) { + auto &yogaStyle = props.yogaStyle; + yogaStyle.display() = YGDisplayNone; + }); + + testViewTree_([](StubViewTree const &viewTree) { + // 5 views in total. + EXPECT_EQ(viewTree.size(), 5); + + // The root view has all 4 subviews. + EXPECT_EQ(viewTree.getRootStubView().children.size(), 4); + + // The root view subviews are [10, 9, 5, 3]. + EXPECT_EQ(viewTree.getRootStubView().children.at(0)->tag, 10); + EXPECT_EQ(viewTree.getRootStubView().children.at(1)->tag, 9); + EXPECT_EQ(viewTree.getRootStubView().children.at(2)->tag, 5); + EXPECT_EQ(viewTree.getRootStubView().children.at(3)->tag, 3); + }); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/android/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index 088814a2a13a8..b88ec2c33f781 100644 --- a/android/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/android/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -10,20 +10,27 @@ #include #include -#include #include +#include #include #include -#include - #include #include #include +#include + using namespace facebook::react; class DummyShadowTreeDelegate : public ShadowTreeDelegate { public: + virtual RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const override { + return newRootShadowNode; + }; + virtual void shadowTreeDidFinishTransaction( ShadowTree const &shadowTree, MountingCoordinator::Shared const &mountingCoordinator) const override{}; @@ -71,15 +78,20 @@ TEST(StateReconciliationTest, testStateReconciliation) { .reference(shadowNodeAB) .children({ Element() - .reference(shadowNodeABA), - Element() - .reference(shadowNodeABB), - Element() - .reference(shadowNodeABC) + .children({ + Element() + .reference(shadowNodeABA), + Element() + .reference(shadowNodeABB), + Element() + .reference(shadowNodeABC) + }) }) }); // clang-format on + ContextContainer contextContainer{}; + auto shadowNode = builder.build(element); auto rootShadowNodeState1 = shadowNode->ShadowNode::clone({}); @@ -88,15 +100,12 @@ TEST(StateReconciliationTest, testStateReconciliation) { auto &family = shadowNodeAB->getFamily(); auto state1 = shadowNodeAB->getState(); auto shadowTreeDelegate = DummyShadowTreeDelegate{}; - auto eventDispatcher = EventDispatcher::Shared{}; - auto rootComponentDescriptor = - ComponentDescriptorParameters{eventDispatcher, nullptr, nullptr}; - ShadowTree shadowTree{SurfaceId{11}, - LayoutConstraints{}, - LayoutContext{}, - rootComponentDescriptor, - shadowTreeDelegate, - {}}; + ShadowTree shadowTree{ + SurfaceId{11}, + LayoutConstraints{}, + LayoutContext{}, + shadowTreeDelegate, + contextContainer}; shadowTree.commit( [&](RootShadowNode const &oldRootShadowNode) { @@ -114,9 +123,10 @@ TEST(StateReconciliationTest, testStateReconciliation) { auto rootShadowNodeState2 = shadowNode->cloneTree(family, [&](ShadowNode const &oldShadowNode) { - return oldShadowNode.clone({ShadowNodeFragment::propsPlaceholder(), - ShadowNodeFragment::childrenPlaceholder(), - state2}); + return oldShadowNode.clone( + {ShadowNodeFragment::propsPlaceholder(), + ShadowNodeFragment::childrenPlaceholder(), + state2}); }); EXPECT_EQ( @@ -136,9 +146,10 @@ TEST(StateReconciliationTest, testStateReconciliation) { auto rootShadowNodeState3 = rootShadowNodeState2->cloneTree( family, [&](ShadowNode const &oldShadowNode) { - return oldShadowNode.clone({ShadowNodeFragment::propsPlaceholder(), - ShadowNodeFragment::childrenPlaceholder(), - state3}); + return oldShadowNode.clone( + {ShadowNodeFragment::propsPlaceholder(), + ShadowNodeFragment::childrenPlaceholder(), + state3}); }); EXPECT_EQ( diff --git a/android/ReactCommon/react/renderer/mounting/tests/TransactionTelemetryTest.cpp b/android/ReactCommon/react/renderer/mounting/tests/TransactionTelemetryTest.cpp deleted file mode 100644 index 51e459441759e..0000000000000 --- a/android/ReactCommon/react/renderer/mounting/tests/TransactionTelemetryTest.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include - -#include -#include - -using namespace facebook::react; - -#define EXPECT_EQ_WITH_THRESHOLD(a, b, threshold) \ - EXPECT_TRUE((a >= b - threshold) && (a <= b + threshold)) - -template -void sleep(double durationInSeconds) { - auto timepoint = ClockT::now() + - std::chrono::milliseconds((long long)(durationInSeconds * 1000)); - while (ClockT::now() < timepoint) { - } -} - -TEST(TransactionTelemetryTest, timepoints) { - auto threshold = int64_t{70}; - - auto timepointA = telemetryTimePointNow(); - sleep(0.1); - auto timepointB = telemetryTimePointNow(); - - auto duration = telemetryDurationToMilliseconds(timepointB - timepointA); - - EXPECT_EQ_WITH_THRESHOLD(duration, 100, threshold); -} - -TEST(TransactionTelemetryTest, normalUseCase) { - auto threshold = int64_t{70}; - auto telemetry = TransactionTelemetry{}; - - telemetry.setAsThreadLocal(); - - telemetry.willCommit(); - sleep(0.1); - telemetry.willLayout(); - sleep(0.2); - - telemetry.didMeasureText(); - TransactionTelemetry::threadLocalTelemetry()->didMeasureText(); - TransactionTelemetry::threadLocalTelemetry()->didMeasureText(); - - telemetry.didLayout(); - sleep(0.1); - telemetry.didCommit(); - - telemetry.setRevisionNumber(42); - - telemetry.unsetAsThreadLocal(); - - sleep(0.3); - - telemetry.willMount(); - sleep(0.1); - telemetry.didMount(); - - auto commitDuration = telemetryDurationToMilliseconds( - telemetry.getCommitEndTime() - telemetry.getCommitStartTime()); - auto layoutDuration = telemetryDurationToMilliseconds( - telemetry.getLayoutEndTime() - telemetry.getLayoutStartTime()); - auto mountDuration = telemetryDurationToMilliseconds( - telemetry.getMountEndTime() - telemetry.getMountStartTime()); - - EXPECT_EQ_WITH_THRESHOLD(commitDuration, 400, threshold); - EXPECT_EQ_WITH_THRESHOLD(layoutDuration, 200, threshold); - EXPECT_EQ_WITH_THRESHOLD(mountDuration, 100, threshold); - - EXPECT_EQ(telemetry.getNumberOfTextMeasurements(), 3); - EXPECT_EQ(telemetry.getRevisionNumber(), 42); -} - -TEST(TransactionTelemetryTest, abnormalUseCases) { - // Calling `did` before `will` should crash. - EXPECT_DEATH_IF_SUPPORTED( - { - auto telemetry = TransactionTelemetry{}; - telemetry.didDiff(); - }, - "diffStartTime_"); - - EXPECT_DEATH_IF_SUPPORTED( - { - auto telemetry = TransactionTelemetry{}; - telemetry.didCommit(); - }, - "commitStartTime_"); - - EXPECT_DEATH_IF_SUPPORTED( - { - auto telemetry = TransactionTelemetry{}; - telemetry.didMount(); - }, - "mountStartTime_"); - - // Getting `start` *or* `end` timepoints before a pair of `will` and `did` - // should crash. - EXPECT_DEATH_IF_SUPPORTED( - { - auto telemetry = TransactionTelemetry{}; - telemetry.willCommit(); - telemetry.getCommitStartTime(); - }, - "commitEndTime_"); - - EXPECT_DEATH_IF_SUPPORTED( - { - auto telemetry = TransactionTelemetry{}; - telemetry.willCommit(); - telemetry.getCommitEndTime(); - }, - "commitEndTime_"); -} diff --git a/android/ReactCommon/react/renderer/runtimescheduler/Android.mk b/android/ReactCommon/react/renderer/runtimescheduler/Android.mk new file mode 100644 index 0000000000000..b67a8e632908e --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/Android.mk @@ -0,0 +1,28 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_render_runtimescheduler + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_SHARED_LIBRARIES := libruntimeexecutor libreact_render_core libreact_debug libjsi callinvoker + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"Fabric\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,runtimeexecutor) +$(call import-module,callinvoker) diff --git a/android/ReactCommon/react/renderer/runtimescheduler/BUCK b/android/ReactCommon/react/renderer/runtimescheduler/BUCK new file mode 100644 index 0000000000000..81f2b99954f91 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/BUCK @@ -0,0 +1,71 @@ +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "fb_xplat_cxx_test", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "runtimescheduler", + srcs = glob( + ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), + ), + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/renderer/runtimescheduler", + ), + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + tests = [":tests"], + visibility = ["PUBLIC"], + deps = [ + react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + react_native_xplat_target("react/renderer/debug:debug"), + react_native_xplat_target("better:better"), + react_native_xplat_target("callinvoker:callinvoker"), + ], +) + +fb_xplat_cxx_test( + name = "tests", + srcs = glob(["tests/*.cpp"]), + headers = glob(["tests/*.h"]), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++17", + "-Wall", + ], + contacts = ["oncall+react_native@xmail.facebook.com"], + platforms = (ANDROID, APPLE, CXX), + deps = [ + react_native_xplat_target("react/renderer/runtimescheduler:runtimescheduler"), + "//xplat/hermes/API:HermesAPI", + "//xplat/third-party/gmock:gtest", + ], +) diff --git a/android/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h b/android/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h new file mode 100644 index 0000000000000..a6da8b49b852a --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/ErrorUtils.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +namespace facebook { +namespace react { + +inline static void handleFatalError( + jsi::Runtime &runtime, + const jsi::JSError &error) { + auto reportFatalError = "reportFatalError"; + auto errorUtils = runtime.global().getProperty(runtime, "ErrorUtils"); + if (errorUtils.isUndefined() || !errorUtils.isObject() || + !errorUtils.getObject(runtime).hasProperty(runtime, reportFatalError)) { + // ErrorUtils was not set up. This probably means the bundle didn't + // load properly. + throw jsi::JSError( + runtime, + "ErrorUtils is not set up properly. Something probably went wrong trying to load the JS bundle. Trying to report error " + + error.getMessage(), + error.getStack()); + } + + auto func = errorUtils.asObject(runtime).getPropertyAsFunction( + runtime, reportFatalError); + + func.call(runtime, error.value()); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp new file mode 100644 index 0000000000000..ecf2589dddcad --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RuntimeScheduler.h" +#include "ErrorUtils.h" + +namespace facebook { +namespace react { + +#pragma mark - Public + +RuntimeScheduler::RuntimeScheduler( + RuntimeExecutor const &runtimeExecutor, + std::function now) + : runtimeExecutor_(runtimeExecutor), now_(now) {} + +void RuntimeScheduler::scheduleWork( + std::function callback) const { + if (enableYielding_) { + shouldYield_ = true; + runtimeExecutor_( + [this, callback = std::move(callback)](jsi::Runtime &runtime) { + shouldYield_ = false; + callback(runtime); + startWorkLoop(runtime); + }); + } else { + runtimeExecutor_([callback = std::move(callback)](jsi::Runtime &runtime) { + callback(runtime); + }); + } +} + +std::shared_ptr RuntimeScheduler::scheduleTask( + SchedulerPriority priority, + jsi::Function callback) { + auto expirationTime = now() + timeoutForSchedulerPriority(priority); + auto task = + std::make_shared(priority, std::move(callback), expirationTime); + taskQueue_.push(task); + + if (!isCallbackScheduled_ && !isPerformingWork_) { + isCallbackScheduled_ = true; + runtimeExecutor_([this](jsi::Runtime &runtime) { + isCallbackScheduled_ = false; + startWorkLoop(runtime); + }); + } + + return task; +} + +bool RuntimeScheduler::getShouldYield() const noexcept { + return shouldYield_; +} + +void RuntimeScheduler::cancelTask(const std::shared_ptr &task) noexcept { + task->callback.reset(); +} + +SchedulerPriority RuntimeScheduler::getCurrentPriorityLevel() const noexcept { + return currentPriority_; +} + +RuntimeSchedulerTimePoint RuntimeScheduler::now() const noexcept { + return now_(); +} + +void RuntimeScheduler::setEnableYielding(bool enableYielding) { + enableYielding_ = enableYielding; +} + +void RuntimeScheduler::executeNowOnTheSameThread( + std::function callback) const { + shouldYield_ = true; + executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor_, + [callback = std::move(callback)](jsi::Runtime &runtime) { + callback(runtime); + }); + shouldYield_ = false; +} + +#pragma mark - Private + +void RuntimeScheduler::startWorkLoop(jsi::Runtime &runtime) const { + auto previousPriority = currentPriority_; + isPerformingWork_ = true; + try { + while (!taskQueue_.empty()) { + auto topPriorityTask = taskQueue_.top(); + auto now = now_(); + auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now; + + if (!didUserCallbackTimeout && shouldYield_) { + // This task hasn't expired and we need to yield. + break; + } + currentPriority_ = topPriorityTask->priority; + auto result = topPriorityTask->execute(runtime); + + if (result.isObject() && result.getObject(runtime).isFunction(runtime)) { + topPriorityTask->callback = + result.getObject(runtime).getFunction(runtime); + } else { + if (taskQueue_.top() == topPriorityTask) { + taskQueue_.pop(); + } + } + } + } catch (jsi::JSError &error) { + handleFatalError(runtime, error); + } + + currentPriority_ = previousPriority; + isPerformingWork_ = false; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h new file mode 100644 index 0000000000000..5d18a7d9ea8a8 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class RuntimeScheduler final { + public: + RuntimeScheduler( + RuntimeExecutor const &runtimeExecutor, + std::function now = + RuntimeSchedulerClock::now); + /* + * Not copyable. + */ + RuntimeScheduler(RuntimeScheduler const &) = delete; + RuntimeScheduler &operator=(RuntimeScheduler const &) = delete; + + /* + * Not movable. + */ + RuntimeScheduler(RuntimeScheduler &&) = delete; + RuntimeScheduler &operator=(RuntimeScheduler &&) = delete; + + void scheduleWork(std::function callback) const; + + /* + * Grants access to the runtime synchronously on the caller's thread. + * + * Shouldn't be called directly. it is expected to be used + * by dispatching a synchronous event via event emitter in your native + * component. + */ + void executeNowOnTheSameThread( + std::function callback) const; + + std::shared_ptr scheduleTask( + SchedulerPriority priority, + jsi::Function callback); + + void cancelTask(std::shared_ptr const &task) noexcept; + + bool getShouldYield() const noexcept; + + SchedulerPriority getCurrentPriorityLevel() const noexcept; + + RuntimeSchedulerTimePoint now() const noexcept; + + void setEnableYielding(bool enableYielding); + + private: + mutable std::priority_queue< + std::shared_ptr, + std::vector>, + TaskPriorityComparer> + taskQueue_; + + RuntimeExecutor const runtimeExecutor_; + mutable SchedulerPriority currentPriority_{SchedulerPriority::NormalPriority}; + mutable std::atomic_bool shouldYield_{false}; + + void startWorkLoop(jsi::Runtime &runtime) const; + + /* + * Returns a time point representing the current point in time. May be called + * from multiple threads. + */ + std::function now_; + + /* + * Flag indicating if callback on JavaScript queue has been + * scheduled. + */ + std::atomic_bool isCallbackScheduled_{false}; + + /* + * Flag indicating if yielding is enabled. + * + * If set to true and Concurrent Mode is enabled on the surface, + * React Native will ask React to yield in case any work has been scheduled. + * Default value is false + */ + bool enableYielding_{false}; + + /* + * This flag is set while performing work, to prevent re-entrancy. + */ + mutable bool isPerformingWork_{false}; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp new file mode 100644 index 0000000000000..92525118e282d --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RuntimeSchedulerBinding.h" +#include "SchedulerPriority.h" +#include "primitives.h" + +#include +#include +#include + +namespace facebook { +namespace react { + +std::shared_ptr +RuntimeSchedulerBinding::createAndInstallIfNeeded( + jsi::Runtime &runtime, + std::shared_ptr const &runtimeScheduler) { + auto runtimeSchedulerModuleName = "nativeRuntimeScheduler"; + + auto runtimeSchedulerValue = + runtime.global().getProperty(runtime, runtimeSchedulerModuleName); + if (runtimeSchedulerValue.isUndefined()) { + // The global namespace does not have an instance of the binding; + // we need to create, install and return it. + auto runtimeSchedulerBinding = + std::make_shared(runtimeScheduler); + auto object = + jsi::Object::createFromHostObject(runtime, runtimeSchedulerBinding); + runtime.global().setProperty( + runtime, runtimeSchedulerModuleName, std::move(object)); + return runtimeSchedulerBinding; + } + + // The global namespace already has an instance of the binding; + // we need to return that. + auto runtimeSchedulerObject = runtimeSchedulerValue.asObject(runtime); + return runtimeSchedulerObject.getHostObject(runtime); +} + +RuntimeSchedulerBinding::RuntimeSchedulerBinding( + std::shared_ptr const &runtimeScheduler) + : runtimeScheduler_(runtimeScheduler) {} + +jsi::Value RuntimeSchedulerBinding::get( + jsi::Runtime &runtime, + jsi::PropNameID const &name) { + auto propertyName = name.utf8(runtime); + + if (propertyName == "unstable_scheduleCallback") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 3, + [this]( + jsi::Runtime &runtime, + jsi::Value const &, + jsi::Value const *arguments, + size_t) noexcept -> jsi::Value { + SchedulerPriority priority = fromRawValue(arguments[0].getNumber()); + auto callback = arguments[1].getObject(runtime).getFunction(runtime); + + auto task = + runtimeScheduler_->scheduleTask(priority, std::move(callback)); + + return valueFromTask(runtime, task); + }); + } + + if (propertyName == "unstable_cancelCallback") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 1, + [this]( + jsi::Runtime &runtime, + jsi::Value const &, + jsi::Value const *arguments, + size_t) noexcept -> jsi::Value { + runtimeScheduler_->cancelTask(taskFromValue(runtime, arguments[0])); + return jsi::Value::undefined(); + }); + } + + if (propertyName == "unstable_shouldYield") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 0, + [this]( + jsi::Runtime &, + jsi::Value const &, + jsi::Value const *, + size_t) noexcept -> jsi::Value { + auto shouldYield = runtimeScheduler_->getShouldYield(); + return jsi::Value(shouldYield); + }); + } + + if (propertyName == "unstable_requestPaint") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 0, + [](jsi::Runtime &, + jsi::Value const &, + jsi::Value const *, + size_t) noexcept -> jsi::Value { + // RequestPaint is left empty by design. + return jsi::Value::undefined(); + }); + } + + if (propertyName == "unstable_now") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 0, + [this]( + jsi::Runtime &, + jsi::Value const &, + jsi::Value const *, + size_t) noexcept -> jsi::Value { + auto now = runtimeScheduler_->now(); + auto asDouble = + std::chrono::duration(now.time_since_epoch()) + .count(); + return jsi::Value(asDouble); + }); + } + + if (propertyName == "unstable_getCurrentPriorityLevel") { + auto currentPriorityLevel = runtimeScheduler_->getCurrentPriorityLevel(); + return jsi::Value(runtime, serialize(currentPriorityLevel)); + } + + if (propertyName == "unstable_ImmediatePriority") { + return jsi::Value(runtime, serialize(SchedulerPriority::ImmediatePriority)); + } + + if (propertyName == "unstable_UserBlockingPriority") { + return jsi::Value( + runtime, serialize(SchedulerPriority::UserBlockingPriority)); + } + + if (propertyName == "unstable_NormalPriority") { + return jsi::Value(runtime, serialize(SchedulerPriority::NormalPriority)); + } + + if (propertyName == "unstable_LowPriority") { + return jsi::Value(runtime, serialize(SchedulerPriority::LowPriority)); + } + + if (propertyName == "unstable_IdlePriority") { + return jsi::Value(runtime, serialize(SchedulerPriority::IdlePriority)); + } + + if (propertyName == "$$typeof") { + return jsi::Value::undefined(); + } + + react_native_assert(false && "undefined property"); + return jsi::Value::undefined(); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h new file mode 100644 index 0000000000000..27c1ad9f31dff --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +/* + * Exposes RuntimeScheduler to JavaScript realm. + */ +class RuntimeSchedulerBinding : public jsi::HostObject { + public: + RuntimeSchedulerBinding( + std::shared_ptr const &runtimeScheduler); + + /* + * Installs RuntimeSchedulerBinding into JavaScript runtime if needed. + * Creates and sets `RuntimeSchedulerBinding` into the global namespace. + * In case if the global namespace already has a `RuntimeSchedulerBinding` + * installed, returns that. + */ + static std::shared_ptr createAndInstallIfNeeded( + jsi::Runtime &runtime, + std::shared_ptr const &runtimeScheduler); + + /* + * `jsi::HostObject` specific overloads. + */ + jsi::Value get(jsi::Runtime &runtime, jsi::PropNameID const &name) override; + + private: + std::shared_ptr runtimeScheduler_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.cpp b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.cpp new file mode 100644 index 0000000000000..70f02ca132e03 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RuntimeSchedulerCallInvoker.h" + +namespace facebook { +namespace react { + +RuntimeSchedulerCallInvoker::RuntimeSchedulerCallInvoker( + std::weak_ptr runtimeScheduler) + : runtimeScheduler_(runtimeScheduler) {} + +void RuntimeSchedulerCallInvoker::invokeAsync(std::function &&func) { + if (auto runtimeScheduler = runtimeScheduler_.lock()) { + runtimeScheduler->scheduleWork( + [func = std::move(func)](jsi::Runtime &) { func(); }); + } +} + +void RuntimeSchedulerCallInvoker::invokeSync(std::function &&func) { + if (auto runtimeScheduler = runtimeScheduler_.lock()) { + runtimeScheduler->executeNowOnTheSameThread( + [func = std::move(func)](jsi::Runtime &) { func(); }); + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h new file mode 100644 index 0000000000000..b40e0cad72784 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +/* + * Exposes RuntimeScheduler to native modules. All calls invonked on JavaScript + * queue from native modules will be funneled through RuntimeScheduler. + */ +class RuntimeSchedulerCallInvoker : public CallInvoker { + public: + RuntimeSchedulerCallInvoker(std::weak_ptr runtimeScheduler); + + void invokeAsync(std::function &&func) override; + void invokeSync(std::function &&func) override; + + private: + /* + * RuntimeScheduler is retained by the runtime. It must not be + * retained by anything beyond the runtime. + */ + std::weak_ptr runtimeScheduler_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerComponentDescriptor.h b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerClock.h similarity index 50% rename from android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerComponentDescriptor.h rename to android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerClock.h index 0c5622241fca7..51652ee0a070d 100644 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDialogPickerComponentDescriptor.h +++ b/android/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerClock.h @@ -7,15 +7,18 @@ #pragma once -#include "AndroidDialogPickerShadowNode.h" - -#include +#include namespace facebook { namespace react { -using AndroidDialogPickerComponentDescriptor = - ConcreteComponentDescriptor; +/* + * Represents a monotonic clock suitable for measuring intervals. + */ +using RuntimeSchedulerClock = std::chrono::steady_clock; + +using RuntimeSchedulerTimePoint = RuntimeSchedulerClock::time_point; +using RuntimeSchedulerDuration = RuntimeSchedulerClock::duration; } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h b/android/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h new file mode 100644 index 0000000000000..329fe252aa386 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/SchedulerPriority.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +enum class SchedulerPriority : int { + ImmediatePriority = 1, + UserBlockingPriority = 2, + NormalPriority = 3, + LowPriority = 4, + IdlePriority = 5, +}; + +static constexpr std::underlying_type::type serialize( + SchedulerPriority schedulerPriority) { + return static_cast::type>( + schedulerPriority); +} + +static inline SchedulerPriority fromRawValue(double value) { + switch ((int)value) { + case 1: + return SchedulerPriority::ImmediatePriority; + case 2: + return SchedulerPriority::UserBlockingPriority; + case 3: + return SchedulerPriority::NormalPriority; + case 4: + return SchedulerPriority::LowPriority; + case 5: + return SchedulerPriority::IdlePriority; + default: + react_native_assert(false && "Unsupported SchedulerPriority value"); + return SchedulerPriority::NormalPriority; + } +} + +static inline std::chrono::milliseconds timeoutForSchedulerPriority( + SchedulerPriority schedulerPriority) { + switch (schedulerPriority) { + case SchedulerPriority::ImmediatePriority: + return std::chrono::milliseconds(-1); + case SchedulerPriority::UserBlockingPriority: + return std::chrono::milliseconds(250); + case SchedulerPriority::NormalPriority: + return std::chrono::milliseconds(5000); + case SchedulerPriority::LowPriority: + return std::chrono::milliseconds(10'000); + case SchedulerPriority::IdlePriority: + return std::chrono::milliseconds::max(); + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/Task.cpp b/android/ReactCommon/react/renderer/runtimescheduler/Task.cpp new file mode 100644 index 0000000000000..bd87944afa37a --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/Task.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RuntimeScheduler.h" + +namespace facebook { +namespace react { + +Task::Task( + SchedulerPriority priority, + jsi::Function callback, + std::chrono::steady_clock::time_point expirationTime) + : priority(priority), + callback(std::move(callback)), + expirationTime(expirationTime) {} + +jsi::Value Task::execute(jsi::Runtime &runtime) { + auto result = jsi::Value::undefined(); + // Cancelled task doesn't have a callback. + if (callback) { + // Callback in JavaScript is expecting a single bool parameter. + // React team plans to remove it and it is safe to pass in + // hardcoded false value. + result = callback.value().call(runtime, {false}); + + // Destroying callback to prevent calling it twice. + callback.reset(); + } + return result; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/Task.h b/android/ReactCommon/react/renderer/runtimescheduler/Task.h new file mode 100644 index 0000000000000..87676d5db737b --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/Task.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class RuntimeScheduler; +class TaskPriorityComparer; + +struct Task final { + Task( + SchedulerPriority priority, + jsi::Function callback, + std::chrono::steady_clock::time_point expirationTime); + + private: + friend RuntimeScheduler; + friend TaskPriorityComparer; + + SchedulerPriority priority; + better::optional callback; + RuntimeSchedulerClock::time_point expirationTime; + + jsi::Value execute(jsi::Runtime &runtime); +}; + +class TaskPriorityComparer { + public: + inline bool operator()( + std::shared_ptr const &lhs, + std::shared_ptr const &rhs) { + return lhs->expirationTime > rhs->expirationTime; + } +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/primitives.h b/android/ReactCommon/react/renderer/runtimescheduler/primitives.h new file mode 100644 index 0000000000000..2283b4cad2710 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/primitives.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +struct TaskWrapper : public jsi::HostObject { + TaskWrapper(std::shared_ptr const &task) : task(task) {} + + std::shared_ptr task; +}; + +inline static jsi::Value valueFromTask( + jsi::Runtime &runtime, + std::shared_ptr const &task) { + return jsi::Object::createFromHostObject( + runtime, std::make_shared(task)); +} + +inline static std::shared_ptr taskFromValue( + jsi::Runtime &runtime, + jsi::Value const &value) { + if (value.isNull()) { + return nullptr; + } + + return value.getObject(runtime).getHostObject(runtime)->task; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp b/android/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp new file mode 100644 index 0000000000000..3fade3594ee6b --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include + +#include "StubClock.h" +#include "StubErrorUtils.h" +#include "StubQueue.h" + +namespace facebook::react { + +using namespace std::chrono_literals; + +class RuntimeSchedulerTest : public testing::Test { + protected: + void SetUp() override { + hostFunctionCallCount_ = 0; + runtime_ = facebook::hermes::makeHermesRuntime(); + stubErrorUtils_ = StubErrorUtils::createAndInstallIfNeeded(*runtime_); + stubQueue_ = std::make_unique(); + + RuntimeExecutor runtimeExecutor = + [this]( + std::function &&callback) { + stubQueue_->runOnQueue([this, callback = std::move(callback)]() { + callback(*runtime_); + }); + }; + + stubClock_ = std::make_unique(StubClock()); + + auto stubNow = [this]() -> RuntimeSchedulerTimePoint { + return stubClock_->getNow(); + }; + + runtimeScheduler_ = + std::make_unique(runtimeExecutor, stubNow); + } + + jsi::Function createHostFunctionFromLambda( + std::function callback) { + return jsi::Function::createFromHostFunction( + *runtime_, + jsi::PropNameID::forUtf8(*runtime_, ""), + 3, + [this, callback = std::move(callback)]( + jsi::Runtime &, + jsi::Value const &, + jsi::Value const *arguments, + size_t) -> jsi::Value { + ++hostFunctionCallCount_; + auto didUserCallbackTimeout = arguments[0].getBool(); + return callback(didUserCallbackTimeout); + }); + } + + uint hostFunctionCallCount_; + + std::unique_ptr runtime_; + std::unique_ptr stubClock_; + std::unique_ptr stubQueue_; + std::unique_ptr runtimeScheduler_; + std::shared_ptr stubErrorUtils_; +}; + +TEST_F(RuntimeSchedulerTest, now) { + stubClock_->setTimePoint(1ms); + + EXPECT_EQ(runtimeScheduler_->now(), RuntimeSchedulerTimePoint(1ms)); + + stubClock_->advanceTimeBy(10ms); + + EXPECT_EQ(runtimeScheduler_->now(), RuntimeSchedulerTimePoint(11ms)); + + stubClock_->advanceTimeBy(6s); + + EXPECT_EQ(runtimeScheduler_->now(), RuntimeSchedulerTimePoint(6011ms)); +} + +TEST_F(RuntimeSchedulerTest, getShouldYield) { + // Always returns false for now. + EXPECT_FALSE(runtimeScheduler_->getShouldYield()); +} + +TEST_F(RuntimeSchedulerTest, scheduleSingleTask) { + bool didRunTask = false; + auto callback = + createHostFunctionFromLambda([&didRunTask](bool didUserCallbackTimeout) { + didRunTask = true; + EXPECT_FALSE(didUserCallbackTimeout); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, scheduleImmediatePriorityTask) { + bool didRunTask = false; + auto callback = + createHostFunctionFromLambda([&didRunTask](bool didUserCallbackTimeout) { + didRunTask = true; + EXPECT_FALSE(didUserCallbackTimeout); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::ImmediatePriority, std::move(callback)); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, taskExpiration) { + bool didRunTask = false; + auto callback = + createHostFunctionFromLambda([&didRunTask](bool didUserCallbackTimeout) { + didRunTask = true; + // Task has timed out but the parameter is deprecated and `false` is + // hardcoded. + EXPECT_FALSE(didUserCallbackTimeout); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + // Task with normal priority has 5s timeout. + stubClock_->advanceTimeBy(6s); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithSamePriority) { + uint firstTaskCallOrder; + auto callbackOne = + createHostFunctionFromLambda([this, &firstTaskCallOrder](bool) { + firstTaskCallOrder = hostFunctionCallCount_; + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callbackOne)); + + uint secondTaskCallOrder; + auto callbackTwo = + createHostFunctionFromLambda([this, &secondTaskCallOrder](bool) { + secondTaskCallOrder = hostFunctionCallCount_; + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callbackTwo)); + + EXPECT_EQ(firstTaskCallOrder, 0); + EXPECT_EQ(secondTaskCallOrder, 0); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_EQ(firstTaskCallOrder, 1); + EXPECT_EQ(secondTaskCallOrder, 2); + EXPECT_EQ(stubQueue_->size(), 0); + EXPECT_EQ(hostFunctionCallCount_, 2); +} + +TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithDifferentPriorities) { + uint lowPriorityTaskCallOrder; + auto callbackOne = + createHostFunctionFromLambda([this, &lowPriorityTaskCallOrder](bool) { + lowPriorityTaskCallOrder = hostFunctionCallCount_; + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::LowPriority, std::move(callbackOne)); + + uint userBlockingPriorityTaskCallOrder; + auto callbackTwo = createHostFunctionFromLambda( + [this, &userBlockingPriorityTaskCallOrder](bool) { + userBlockingPriorityTaskCallOrder = hostFunctionCallCount_; + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::UserBlockingPriority, std::move(callbackTwo)); + + EXPECT_EQ(lowPriorityTaskCallOrder, 0); + EXPECT_EQ(userBlockingPriorityTaskCallOrder, 0); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_EQ(lowPriorityTaskCallOrder, 2); + EXPECT_EQ(userBlockingPriorityTaskCallOrder, 1); + EXPECT_EQ(stubQueue_->size(), 0); + EXPECT_EQ(hostFunctionCallCount_, 2); +} + +TEST_F(RuntimeSchedulerTest, cancelTask) { + bool didRunTask = false; + auto callback = createHostFunctionFromLambda([&didRunTask](bool) { + didRunTask = true; + return jsi::Value::undefined(); + }); + + auto task = runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 1); + + runtimeScheduler_->cancelTask(task); + + stubQueue_->tick(); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, continuationTask) { + bool didRunTask = false; + bool didContinuationTask = false; + + auto callback = createHostFunctionFromLambda([&](bool) { + didRunTask = true; + return jsi::Function::createFromHostFunction( + *runtime_, + jsi::PropNameID::forUtf8(*runtime_, ""), + 1, + [&](jsi::Runtime &runtime, + jsi::Value const &, + jsi::Value const *arguments, + size_t) noexcept -> jsi::Value { + didContinuationTask = true; + return jsi::Value::undefined(); + }); + }); + + auto task = runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(didRunTask); + EXPECT_TRUE(didContinuationTask); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, getCurrentPriorityLevel) { + auto callback = + createHostFunctionFromLambda([this](bool didUserCallbackTimeout) { + EXPECT_EQ( + runtimeScheduler_->getCurrentPriorityLevel(), + SchedulerPriority::ImmediatePriority); + return jsi::Value::undefined(); + }); + + EXPECT_EQ( + runtimeScheduler_->getCurrentPriorityLevel(), + SchedulerPriority::NormalPriority); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::ImmediatePriority, std::move(callback)); + + stubQueue_->tick(); + + EXPECT_EQ( + runtimeScheduler_->getCurrentPriorityLevel(), + SchedulerPriority::NormalPriority); + + callback = createHostFunctionFromLambda([this](bool didUserCallbackTimeout) { + EXPECT_EQ( + runtimeScheduler_->getCurrentPriorityLevel(), + SchedulerPriority::IdlePriority); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::IdlePriority, std::move(callback)); + + stubQueue_->tick(); + + EXPECT_EQ( + runtimeScheduler_->getCurrentPriorityLevel(), + SchedulerPriority::NormalPriority); +} + +TEST_F(RuntimeSchedulerTest, scheduleWork) { + bool wasCalled = false; + runtimeScheduler_->scheduleWork( + [&](jsi::Runtime const &) { wasCalled = true; }); + + EXPECT_FALSE(wasCalled); + + EXPECT_FALSE(runtimeScheduler_->getShouldYield()); + + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(wasCalled); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, scheduleWorkWithYielding) { + runtimeScheduler_->setEnableYielding(true); + bool wasCalled = false; + runtimeScheduler_->scheduleWork( + [&](jsi::Runtime const &) { wasCalled = true; }); + + EXPECT_FALSE(wasCalled); + + EXPECT_TRUE(runtimeScheduler_->getShouldYield()); + + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(wasCalled); + EXPECT_FALSE(runtimeScheduler_->getShouldYield()); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, normalTaskYieldsToPlatformEvent) { + runtimeScheduler_->setEnableYielding(true); + + bool didRunJavaScriptTask = false; + bool didRunPlatformWork = false; + + auto callback = createHostFunctionFromLambda([&](bool) { + didRunJavaScriptTask = true; + EXPECT_TRUE(didRunPlatformWork); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + runtimeScheduler_->scheduleWork([&](jsi::Runtime const &) { + didRunPlatformWork = true; + EXPECT_FALSE(didRunJavaScriptTask); + EXPECT_FALSE(runtimeScheduler_->getShouldYield()); + }); + + EXPECT_TRUE(runtimeScheduler_->getShouldYield()); + EXPECT_EQ(stubQueue_->size(), 2); + + stubQueue_->flush(); + + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, expiredTaskDoesntYieldToPlatformEvent) { + runtimeScheduler_->setEnableYielding(true); + + bool didRunJavaScriptTask = false; + bool didRunPlatformWork = false; + + auto callback = createHostFunctionFromLambda([&](bool) { + didRunJavaScriptTask = true; + EXPECT_FALSE(didRunPlatformWork); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback)); + + runtimeScheduler_->scheduleWork([&](jsi::Runtime const &) { + didRunPlatformWork = true; + EXPECT_TRUE(didRunJavaScriptTask); + }); + + EXPECT_TRUE(runtimeScheduler_->getShouldYield()); + EXPECT_EQ(stubQueue_->size(), 2); + + stubClock_->advanceTimeBy(6s); + + stubQueue_->flush(); + + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, immediateTaskDoesntYieldToPlatformEvent) { + runtimeScheduler_->setEnableYielding(true); + + bool didRunJavaScriptTask = false; + bool didRunPlatformWork = false; + + auto callback = createHostFunctionFromLambda([&](bool) { + didRunJavaScriptTask = true; + EXPECT_FALSE(didRunPlatformWork); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::ImmediatePriority, std::move(callback)); + + runtimeScheduler_->scheduleWork([&](jsi::Runtime const &) { + didRunPlatformWork = true; + EXPECT_TRUE(didRunJavaScriptTask); + }); + + EXPECT_TRUE(runtimeScheduler_->getShouldYield()); + EXPECT_EQ(stubQueue_->size(), 2); + + stubQueue_->flush(); + + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, scheduleTaskFromTask) { + bool didRunFirstTask = false; + bool didRunSecondTask = false; + auto firstCallback = createHostFunctionFromLambda( + [this, &didRunFirstTask, &didRunSecondTask](bool didUserCallbackTimeout) { + didRunFirstTask = true; + EXPECT_FALSE(didUserCallbackTimeout); + + auto secondCallback = createHostFunctionFromLambda( + [&didRunSecondTask](bool didUserCallbackTimeout) { + didRunSecondTask = true; + EXPECT_FALSE(didUserCallbackTimeout); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::ImmediatePriority, std::move(secondCallback)); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(firstCallback)); + + EXPECT_FALSE(didRunFirstTask); + EXPECT_FALSE(didRunSecondTask); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(didRunFirstTask); + EXPECT_TRUE(didRunSecondTask); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(RuntimeSchedulerTest, handlingError) { + bool didRunTask = false; + auto firstCallback = createHostFunctionFromLambda([this, &didRunTask](bool) { + didRunTask = true; + jsi::detail::throwJSError(*runtime_, "Test error"); + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(firstCallback)); + + EXPECT_FALSE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 1); + + stubQueue_->tick(); + + EXPECT_TRUE(didRunTask); + EXPECT_EQ(stubQueue_->size(), 0); + EXPECT_EQ(stubErrorUtils_->getReportFatalCallCount(), 1); +} + +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/runtimescheduler/tests/SchedulerPriorityTest.cpp b/android/ReactCommon/react/renderer/runtimescheduler/tests/SchedulerPriorityTest.cpp new file mode 100644 index 0000000000000..4139e2f0c16cb --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/tests/SchedulerPriorityTest.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +using namespace facebook::react; + +TEST(SchedulerPriorityTest, fromRawValue) { + EXPECT_EQ(SchedulerPriority::ImmediatePriority, fromRawValue(1.0)); + EXPECT_EQ(SchedulerPriority::UserBlockingPriority, fromRawValue(2.0)); + EXPECT_EQ(SchedulerPriority::NormalPriority, fromRawValue(3.0)); + EXPECT_EQ(SchedulerPriority::LowPriority, fromRawValue(4.0)); + EXPECT_EQ(SchedulerPriority::IdlePriority, fromRawValue(5.0)); +} + +TEST(SchedulerPriorityTest, serialize) { + EXPECT_EQ(serialize(SchedulerPriority::ImmediatePriority), 1); + EXPECT_EQ(serialize(SchedulerPriority::UserBlockingPriority), 2); + EXPECT_EQ(serialize(SchedulerPriority::NormalPriority), 3); + EXPECT_EQ(serialize(SchedulerPriority::LowPriority), 4); + EXPECT_EQ(serialize(SchedulerPriority::IdlePriority), 5); +} + +TEST(SchedulerPriorityTest, timeoutForSchedulerPriority) { + EXPECT_EQ( + timeoutForSchedulerPriority(SchedulerPriority::ImmediatePriority), + std::chrono::milliseconds(-1)); + EXPECT_EQ( + timeoutForSchedulerPriority(SchedulerPriority::UserBlockingPriority), + std::chrono::milliseconds(250)); + EXPECT_EQ( + timeoutForSchedulerPriority(SchedulerPriority::NormalPriority), + std::chrono::seconds(5)); + EXPECT_EQ( + timeoutForSchedulerPriority(SchedulerPriority::LowPriority), + std::chrono::seconds(10)); + EXPECT_EQ( + timeoutForSchedulerPriority(SchedulerPriority::IdlePriority), + std::chrono::milliseconds::max()); +} diff --git a/android/ReactCommon/react/renderer/runtimescheduler/tests/StubClock.h b/android/ReactCommon/react/renderer/runtimescheduler/tests/StubClock.h new file mode 100644 index 0000000000000..6ac977fd7d536 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/tests/StubClock.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::react { + +class StubClock { + public: + RuntimeSchedulerTimePoint getNow() const { + return timePoint_; + } + + void setTimePoint(RuntimeSchedulerTimePoint timePoint) { + timePoint_ = timePoint; + } + + void setTimePoint(RuntimeSchedulerDuration duration) { + timePoint_ = RuntimeSchedulerTimePoint(duration); + } + + RuntimeSchedulerTimePoint getTimePoint() { + return timePoint_; + } + + void advanceTimeBy(RuntimeSchedulerDuration duration) { + timePoint_ += duration; + } + + private: + RuntimeSchedulerTimePoint timePoint_; +}; + +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/runtimescheduler/tests/StubErrorUtils.h b/android/ReactCommon/react/renderer/runtimescheduler/tests/StubErrorUtils.h new file mode 100644 index 0000000000000..ca2de84a0fe61 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/tests/StubErrorUtils.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +/* + * Exposes StubErrorUtils to JavaScript realm. + */ +class StubErrorUtils : public jsi::HostObject { + public: + static std::shared_ptr createAndInstallIfNeeded( + jsi::Runtime &runtime) { + auto errorUtilsModuleName = "ErrorUtils"; + auto errorUtilsValue = + runtime.global().getProperty(runtime, errorUtilsModuleName); + + if (errorUtilsValue.isUndefined()) { + auto stubErrorUtils = std::make_shared(); + auto object = jsi::Object::createFromHostObject(runtime, stubErrorUtils); + runtime.global().setProperty( + runtime, errorUtilsModuleName, std::move(object)); + return stubErrorUtils; + } + + auto stubErrorUtilsObject = errorUtilsValue.asObject(runtime); + return stubErrorUtilsObject.getHostObject(runtime); + } + + /* + * `jsi::HostObject` specific overloads. + */ + jsi::Value get(jsi::Runtime &runtime, jsi::PropNameID const &name) override { + auto propertyName = name.utf8(runtime); + + if (propertyName == "reportFatalError") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 1, + [this]( + jsi::Runtime &runtime, + jsi::Value const &, + jsi::Value const *arguments, + size_t) noexcept -> jsi::Value { + reportFatalCallCount_++; + return jsi::Value::undefined(); + }); + } + + return jsi::Value::undefined(); + } + + int getReportFatalCallCount() const { + return reportFatalCallCount_; + } + + private: + int reportFatalCallCount_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/runtimescheduler/tests/StubQueue.h b/android/ReactCommon/react/renderer/runtimescheduler/tests/StubQueue.h new file mode 100644 index 0000000000000..272be4833b006 --- /dev/null +++ b/android/ReactCommon/react/renderer/runtimescheduler/tests/StubQueue.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +class StubQueue { + public: + void runOnQueue(std::function &&func) { + callbackQueue_.push(func); + } + + void flush() { + while (!callbackQueue_.empty()) { + tick(); + } + } + + void tick() { + if (!callbackQueue_.empty()) { + auto callback = callbackQueue_.front(); + callback(); + callbackQueue_.pop(); + } + } + + int size() { + return callbackQueue_.size(); + } + + private: + std::queue> callbackQueue_; +}; diff --git a/android/ReactCommon/react/renderer/scheduler/Android.mk b/android/ReactCommon/react/renderer/scheduler/Android.mk index e1599588bba3c..32b8f71dd0478 100644 --- a/android/ReactCommon/react/renderer/scheduler/Android.mk +++ b/android/ReactCommon/react/renderer/scheduler/Android.mk @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libyoga libreact_render_components_view libreact_utils libreact_render_templateprocessor libreact_render_graphics libreact_render_uimanager libfolly_futures libreact_render_componentregistry glog libreactconfig libfolly_json libjsi libreact_render_core libreact_render_debug libreact_render_components_root libreact_render_mounting +LOCAL_SHARED_LIBRARIES := libyoga librrc_view libreact_utils libreact_render_templateprocessor libreact_render_graphics libreact_render_uimanager libfolly_futures libreact_render_componentregistry glog libreactconfig libfolly_json libjsi libreact_render_core libreact_render_debug librrc_root libreact_render_mounting libreact_debug libreact_render_runtimescheduler include $(BUILD_SHARED_LIBRARY) @@ -37,6 +37,8 @@ $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/mounting) $(call import-module,react/renderer/uimanager) +$(call import-module,react/renderer/runtimescheduler) $(call import-module,react/renderer/templateprocessor) $(call import-module,react/utils) +$(call import-module,react/debug) $(call import-module,yogajni) diff --git a/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.cpp b/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.cpp index 2649227d327ff..e625afe059bbf 100644 --- a/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.cpp +++ b/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.cpp @@ -7,8 +7,9 @@ #include "AsynchronousEventBeat.h" -namespace facebook { -namespace react { +#include + +namespace facebook::react { AsynchronousEventBeat::AsynchronousEventBeat( RunLoopObserver::Unique uiRunLoopObserver, @@ -22,34 +23,36 @@ AsynchronousEventBeat::AsynchronousEventBeat( void AsynchronousEventBeat::activityDidChange( RunLoopObserver::Delegate const *delegate, - RunLoopObserver::Activity activity) const noexcept { - assert(delegate == this); + RunLoopObserver::Activity) const noexcept { + react_native_assert(delegate == this); induce(); } void AsynchronousEventBeat::induce() const { - if (!isRequested_) { + if (!isRequested_ || isBeatCallbackScheduled_) { return; } + isRequested_ = false; + // Here we know that `this` object exists because the caller has a strong // pointer to `owner`. To ensure the object will exist inside // `runtimeExecutor_` callback, we need to copy the pointer there. auto weakOwner = uiRunLoopObserver_->getOwner(); - runtimeExecutor_([this, weakOwner](jsi::Runtime &runtime) mutable { + isBeatCallbackScheduled_ = true; + + runtimeExecutor_([this, weakOwner](jsi::Runtime &runtime) { + isBeatCallbackScheduled_ = false; + auto owner = weakOwner.lock(); if (!owner) { return; } - if (!isRequested_) { - return; + if (beatCallback_) { + beatCallback_(runtime); } - - this->beat(runtime); }); } - -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.h b/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.h index 1acdcbb02d038..c6751a087912b 100644 --- a/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.h +++ b/android/ReactCommon/react/renderer/scheduler/AsynchronousEventBeat.h @@ -9,8 +9,7 @@ #include #include -namespace facebook { -namespace react { +namespace facebook::react { /* * Event beat associated with JavaScript runtime. @@ -35,7 +34,8 @@ class AsynchronousEventBeat : public EventBeat, private: RunLoopObserver::Unique uiRunLoopObserver_; RuntimeExecutor runtimeExecutor_; + + mutable std::atomic isBeatCallbackScheduled_{false}; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/scheduler/BUCK b/android/ReactCommon/react/renderer/scheduler/BUCK index 8c525383da061..ee76e263bb1df 100644 --- a/android/ReactCommon/react/renderer/scheduler/BUCK +++ b/android/ReactCommon/react/renderer/scheduler/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -6,6 +5,7 @@ load( "CXX", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -30,12 +30,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/scheduler", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -55,11 +49,13 @@ rn_xplat_cxx_library( "//xplat/folly:molly", "//xplat/jsi:JSIDynamic", "//xplat/jsi:jsi", + react_native_xplat_target("react/config:config"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/uimanager:uimanager"), + react_native_xplat_target("react/renderer/runtimescheduler:runtimescheduler"), react_native_xplat_target("react/renderer/templateprocessor:templateprocessor"), - react_native_xplat_target("react/config:config"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/components/root:root"), diff --git a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.cpp b/android/ReactCommon/react/renderer/scheduler/InspectorData.h similarity index 51% rename from android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.cpp rename to android/ReactCommon/react/renderer/scheduler/InspectorData.h index 724a2fde03e7e..ac77165b0dd0e 100644 --- a/android/ReactCommon/react/renderer/components/picker/androidpicker/react/renderer/components/androidpicker/AndroidDropdownPickerShadowNode.cpp +++ b/android/ReactCommon/react/renderer/scheduler/InspectorData.h @@ -5,13 +5,20 @@ * LICENSE file in the root directory of this source tree. */ -#include "AndroidDropdownPickerShadowNode.h" +#include namespace facebook { namespace react { -extern const char AndroidDropdownPickerComponentName[] = - "AndroidDropdownPicker"; +struct InspectorData { + std::vector hierarchy; + int selectedIndex; + std::string fileName; + int lineNumber; + int columnNumber; + // TODO T97216348: remove folly::dynamic from InspectorData struct + folly::dynamic props; +}; } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/android/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 1f0c617ef8d58..15cff205ffee6 100644 --- a/android/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/android/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -10,11 +10,14 @@ #include #include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -32,16 +35,18 @@ Scheduler::Scheduler( UIManagerAnimationDelegate *animationDelegate, SchedulerDelegate *delegate) { runtimeExecutor_ = schedulerToolbox.runtimeExecutor; + contextContainer_ = schedulerToolbox.contextContainer; reactNativeConfig_ = - schedulerToolbox.contextContainer - ->at>("ReactNativeConfig"); + contextContainer_->at>( + "ReactNativeConfig"); // Creating a container for future `EventDispatcher` instance. eventDispatcher_ = std::make_shared>(); - auto uiManager = std::make_shared(); + auto uiManager = std::make_shared( + runtimeExecutor_, schedulerToolbox.backgroundExecutor, contextContainer_); auto eventOwnerBox = std::make_shared(); eventOwnerBox->owner = eventDispatcher_; @@ -49,11 +54,14 @@ Scheduler::Scheduler( jsi::Runtime &runtime, const EventTarget *eventTarget, const std::string &type, + ReactEventPriority priority, const ValueFactory &payloadFactory) { - uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) { - uiManagerBinding.dispatchEvent( - runtime, eventTarget, type, payloadFactory); - }); + uiManager->visitBinding( + [&](UIManagerBinding const &uiManagerBinding) { + uiManagerBinding.dispatchEvent( + runtime, eventTarget, type, priority, payloadFactory); + }, + runtime); }; auto statePipe = [uiManager](StateUpdate const &stateUpdate) { @@ -63,8 +71,7 @@ Scheduler::Scheduler( // Creating an `EventDispatcher` instance inside the already allocated // container (inside the optional). eventDispatcher_->emplace( - eventPipe, - statePipe, + EventQueueProcessor(eventPipe, statePipe), schedulerToolbox.synchronousEventBeatFactory, schedulerToolbox.asynchronousEventBeatFactory, eventOwnerBox); @@ -74,31 +81,34 @@ Scheduler::Scheduler( EventDispatcher::Shared{eventDispatcher_, &eventDispatcher_->value()}; componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory( - eventDispatcher, schedulerToolbox.contextContainer); + eventDispatcher, contextContainer_); - rootComponentDescriptor_ = std::make_unique( - ComponentDescriptorParameters{eventDispatcher, nullptr, nullptr}); - - uiManager->setBackgroundExecutor(schedulerToolbox.backgroundExecutor); uiManager->setDelegate(this); uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_); - runtimeExecutor_([=](jsi::Runtime &runtime) { - auto uiManagerBinding = UIManagerBinding::createAndInstallIfNeeded(runtime); + runtimeExecutor_([uiManager, + runtimeExecutor = runtimeExecutor_](jsi::Runtime &runtime) { + auto uiManagerBinding = + UIManagerBinding::createAndInstallIfNeeded(runtime, runtimeExecutor); uiManagerBinding->attach(uiManager); }); auto componentDescriptorRegistryKey = "ComponentDescriptorRegistry_DO_NOT_USE_PRETTY_PLEASE"; - schedulerToolbox.contextContainer->erase(componentDescriptorRegistryKey); - schedulerToolbox.contextContainer->insert( + contextContainer_->erase(componentDescriptorRegistryKey); + contextContainer_->insert( componentDescriptorRegistryKey, std::weak_ptr( componentDescriptorRegistry_)); delegate_ = delegate; + commitHooks_ = schedulerToolbox.commitHooks; uiManager_ = uiManager; + for (auto commitHook : commitHooks_) { + uiManager->registerCommitHook(*commitHook); + } + if (animationDelegate != nullptr) { animationDelegate->setComponentDescriptorRegistry( componentDescriptorRegistry_); @@ -106,21 +116,11 @@ Scheduler::Scheduler( uiManager_->setAnimationDelegate(animationDelegate); #ifdef ANDROID - enableReparentingDetection_ = reactNativeConfig_->getBool( - "react_fabric:enable_reparenting_detection_android"); removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( "react_fabric:remove_outstanding_surfaces_on_destruction_android"); - uiManager_->experimentEnableStateUpdateWithAutorepeat = - reactNativeConfig_->getBool( - "react_fabric:enable_state_update_with_autorepeat_android"); #else - enableReparentingDetection_ = reactNativeConfig_->getBool( - "react_fabric:enable_reparenting_detection_ios"); removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( "react_fabric:remove_outstanding_surfaces_on_destruction_ios"); - uiManager_->experimentEnableStateUpdateWithAutorepeat = - reactNativeConfig_->getBool( - "react_fabric:enable_state_update_with_autorepeat_ios"); #endif } @@ -128,6 +128,10 @@ Scheduler::~Scheduler() { LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this << ")."; + for (auto commitHook : commitHooks_) { + uiManager_->unregisterCommitHook(*commitHook); + } + // All Surfaces must be explicitly stopped before destroying `Scheduler`. // The idea is that `UIManager` is allowed to call `Scheduler` only if the // corresponding `ShadowTree` instance exists. @@ -143,9 +147,10 @@ Scheduler::~Scheduler() { surfaceIds.push_back(shadowTree.getSurfaceId()); }); - assert( - surfaceIds.empty() && - "Scheduler was destroyed with outstanding Surfaces."); + // TODO(T88046056): Fix Android memory leak before uncommenting changes + // react_native_assert( + // surfaceIds.empty() && + // "Scheduler was destroyed with outstanding Surfaces."); if (surfaceIds.empty()) { return; @@ -175,35 +180,46 @@ Scheduler::~Scheduler() { } } -void Scheduler::startSurface( - SurfaceId surfaceId, - const std::string &moduleName, - const folly::dynamic &initialProps, - const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext, - std::weak_ptr mountingOverrideDelegate) - const { - SystraceSection s("Scheduler::startSurface"); - - auto shadowTree = std::make_unique( - surfaceId, - layoutConstraints, - layoutContext, - *rootComponentDescriptor_, - *uiManager_, - mountingOverrideDelegate, - enableReparentingDetection_); - - auto uiManager = uiManager_; - - uiManager->getShadowTreeRegistry().add(std::move(shadowTree)); - - runtimeExecutor_([=](jsi::Runtime &runtime) { - uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) { - uiManagerBinding.startSurface( - runtime, surfaceId, moduleName, initialProps); - }); - }); +void Scheduler::registerSurface( + SurfaceHandler const &surfaceHandler) const noexcept { + surfaceHandler.setContextContainer(getContextContainer()); + surfaceHandler.setUIManager(uiManager_.get()); +} + +InspectorData Scheduler::getInspectorDataForInstance( + EventEmitter const &eventEmitter) const noexcept { + return executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor_, [=](jsi::Runtime &runtime) -> InspectorData { + auto uiManagerBinding = UIManagerBinding::getBinding(runtime); + auto value = uiManagerBinding->getInspectorDataForInstance( + runtime, eventEmitter); + + // TODO T97216348: avoid transforming jsi into folly::dynamic + auto dynamic = jsi::dynamicFromValue(runtime, value); + auto source = dynamic["source"]; + + InspectorData result = {}; + result.fileName = + source["fileName"].isNull() ? "" : source["fileName"].c_str(); + result.lineNumber = (int)source["lineNumber"].getDouble(); + result.columnNumber = (int)source["columnNumber"].getDouble(); + result.selectedIndex = (int)dynamic["selectedIndex"].getDouble(); + // TODO T97216348: remove folly::dynamic from InspectorData struct + result.props = dynamic["props"]; + auto hierarchy = dynamic["hierarchy"]; + for (size_t i = 0; i < hierarchy.size(); i++) { + auto viewHierarchyValue = hierarchy[i]["name"]; + if (!viewHierarchyValue.isNull()) { + result.hierarchy.push_back(viewHierarchyValue.c_str()); + } + } + return result; + }); +} + +void Scheduler::unregisterSurface( + SurfaceHandler const &surfaceHandler) const noexcept { + surfaceHandler.setUIManager(nullptr); } void Scheduler::renderTemplateToSurface( @@ -243,77 +259,6 @@ void Scheduler::renderTemplateToSurface( } } -void Scheduler::stopSurface(SurfaceId surfaceId) const { - SystraceSection s("Scheduler::stopSurface"); - - // Stop any ongoing animations. - uiManager_->stopSurfaceForAnimationDelegate(surfaceId); - - // Waiting for all concurrent commits to be finished and unregistering the - // `ShadowTree`. - auto shadowTree = uiManager_->getShadowTreeRegistry().remove(surfaceId); - - // As part of stopping a Surface, we need to properly destroy all - // mounted views, so we need to commit an empty tree to trigger all - // side-effects (including destroying and removing mounted views). - if (shadowTree) { - shadowTree->commitEmptyTree(); - } - - // We execute JavaScript/React part of the process at the very end to minimize - // any visible side-effects of stopping the Surface. Any possible commits from - // the JavaScript side will not be able to reference a `ShadowTree` and will - // fail silently. - auto uiManager = uiManager_; - runtimeExecutor_([=](jsi::Runtime &runtime) { - uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) { - uiManagerBinding.stopSurface(runtime, surfaceId); - }); - }); -} - -Size Scheduler::measureSurface( - SurfaceId surfaceId, - const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext) const { - SystraceSection s("Scheduler::measureSurface"); - - auto currentRootShadowNode = RootShadowNode::Shared{}; - uiManager_->getShadowTreeRegistry().visit( - surfaceId, [&](const ShadowTree &shadowTree) { - currentRootShadowNode = shadowTree.getCurrentRevision().rootShadowNode; - }); - - auto rootShadowNode = - currentRootShadowNode->clone(layoutConstraints, layoutContext); - rootShadowNode->layoutIfNeeded(); - return rootShadowNode->getLayoutMetrics().frame.size; -} - -MountingCoordinator::Shared Scheduler::findMountingCoordinator( - SurfaceId surfaceId) const { - MountingCoordinator::Shared mountingCoordinator = nullptr; - uiManager_->getShadowTreeRegistry().visit( - surfaceId, [&](const ShadowTree &shadowTree) { - mountingCoordinator = shadowTree.getMountingCoordinator(); - }); - return mountingCoordinator; -} - -void Scheduler::constraintSurfaceLayout( - SurfaceId surfaceId, - const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext) const { - SystraceSection s("Scheduler::constraintSurfaceLayout"); - - uiManager_->getShadowTreeRegistry().visit( - surfaceId, [&](ShadowTree const &shadowTree) { - shadowTree.commit([&](RootShadowNode const &oldRootShadowNode) { - return oldRootShadowNode.clone(layoutConstraints, layoutContext); - }); - }); -} - ComponentDescriptor const * Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN( ComponentHandle handle) const { @@ -347,14 +292,23 @@ void Scheduler::uiManagerDidFinishTransaction( delegate_->schedulerDidFinishTransaction(mountingCoordinator); } } -void Scheduler::uiManagerDidCreateShadowNode( - const ShadowNode::Shared &shadowNode) { +void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode &shadowNode) { SystraceSection s("Scheduler::uiManagerDidCreateShadowNode"); if (delegate_) { - auto shadowView = ShadowView(*shadowNode); delegate_->schedulerDidRequestPreliminaryViewAllocation( - shadowNode->getSurfaceId(), shadowView); + shadowNode.getSurfaceId(), shadowNode); + } +} + +void Scheduler::uiManagerDidCloneShadowNode( + const ShadowNode &oldShadowNode, + const ShadowNode &newShadowNode) { + SystraceSection s("Scheduler::uiManagerDidCloneShadowNode"); + + if (delegate_) { + delegate_->schedulerDidCloneShadowNode( + newShadowNode.getSurfaceId(), oldShadowNode, newShadowNode); } } @@ -370,30 +324,33 @@ void Scheduler::uiManagerDidDispatchCommand( } } -/* - * Set JS responder for a view - */ -void Scheduler::uiManagerDidSetJSResponder( - SurfaceId surfaceId, +void Scheduler::uiManagerDidSendAccessibilityEvent( const ShadowNode::Shared &shadowNode, - bool blockNativeResponder) { + std::string const &eventType) { + SystraceSection s("Scheduler::uiManagerDidSendAccessibilityEvent"); + if (delegate_) { - // TODO: the first shadowView paramenter, should be the first parent that - // is non virtual. auto shadowView = ShadowView(*shadowNode); - delegate_->schedulerDidSetJSResponder( - surfaceId, shadowView, shadowView, blockNativeResponder); + delegate_->schedulerDidSendAccessibilityEvent(shadowView, eventType); } } /* - * Clear the JSResponder for a view + * Set JS responder for a view. */ -void Scheduler::uiManagerDidClearJSResponder() { +void Scheduler::uiManagerDidSetIsJSResponder( + ShadowNode::Shared const &shadowNode, + bool isJSResponder, + bool blockNativeResponder) { if (delegate_) { - delegate_->schedulerDidClearJSResponder(); + delegate_->schedulerDidSetIsJSResponder( + ShadowView(*shadowNode), isJSResponder, blockNativeResponder); } } +ContextContainer::Shared Scheduler::getContextContainer() const { + return contextContainer_; +} + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/scheduler/Scheduler.h b/android/ReactCommon/react/renderer/scheduler/Scheduler.h index 3b5693cb72409..32beafc7e9ffd 100644 --- a/android/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/android/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -15,10 +15,13 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -40,50 +43,30 @@ class Scheduler final : public UIManagerDelegate { #pragma mark - Surface Management - void startSurface( - SurfaceId surfaceId, - const std::string &moduleName, - const folly::dynamic &initialProps, - const LayoutConstraints &layoutConstraints = {}, - const LayoutContext &layoutContext = {}, - std::weak_ptr mountingOverrideDelegate = - {}) const; + /* + * Registers and unregisters a `SurfaceHandler` object in the `Scheduler`. + * All registered `SurfaceHandler` objects must be unregistered + * (with the same `Scheduler`) before their deallocation. + */ + void registerSurface(SurfaceHandler const &surfaceHandler) const noexcept; + void unregisterSurface(SurfaceHandler const &surfaceHandler) const noexcept; + + InspectorData getInspectorDataForInstance( + EventEmitter const &eventEmitter) const noexcept; void renderTemplateToSurface( SurfaceId surfaceId, const std::string &uiTemplate); - void stopSurface(SurfaceId surfaceId) const; - - Size measureSurface( - SurfaceId surfaceId, - const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext) const; - - /* - * Applies given `layoutConstraints` and `layoutContext` to a Surface. - * The user interface will be relaid out as a result. The operation will be - * performed synchronously (including mounting) if the method is called - * on the main thread. - * Can be called from any thread. - */ - void constraintSurfaceLayout( - SurfaceId surfaceId, - const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext) const; - /* * This is broken. Please do not use. * `ComponentDescriptor`s are not designed to be used outside of `UIManager`, - * there is no any garantees about their lifetime. + * there is no any guarantees about their lifetime. */ ComponentDescriptor const * findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN( ComponentHandle handle) const; - MountingCoordinator::Shared findMountingCoordinator( - SurfaceId surfaceId) const; - #pragma mark - Delegate /* @@ -105,26 +88,36 @@ class Scheduler final : public UIManagerDelegate { void uiManagerDidFinishTransaction( MountingCoordinator::Shared const &mountingCoordinator) override; - void uiManagerDidCreateShadowNode( - const ShadowNode::Shared &shadowNode) override; + void uiManagerDidCreateShadowNode(const ShadowNode &shadowNode) override; + void uiManagerDidCloneShadowNode( + const ShadowNode &oldShadowNode, + const ShadowNode &newShadowNode) override; void uiManagerDidDispatchCommand( const ShadowNode::Shared &shadowNode, std::string const &commandName, folly::dynamic const args) override; - void uiManagerDidSetJSResponder( - SurfaceId surfaceId, - const ShadowNode::Shared &shadowView, + void uiManagerDidSendAccessibilityEvent( + const ShadowNode::Shared &shadowNode, + std::string const &eventType) override; + void uiManagerDidSetIsJSResponder( + ShadowNode::Shared const &shadowView, + bool isJSResponder, bool blockNativeResponder) override; - void uiManagerDidClearJSResponder() override; + +#pragma mark - ContextContainer + ContextContainer::Shared getContextContainer() const; private: + friend class SurfaceHandler; + SchedulerDelegate *delegate_; SharedComponentDescriptorRegistry componentDescriptorRegistry_; - std::unique_ptr rootComponentDescriptor_; RuntimeExecutor runtimeExecutor_; std::shared_ptr uiManager_; std::shared_ptr reactNativeConfig_; + std::vector> commitHooks_; + /* * At some point, we have to have an owning shared pointer to something that * will become an `EventDispatcher` a moment later. That's why we have it as a @@ -134,10 +127,15 @@ class Scheduler final : public UIManagerDelegate { */ std::shared_ptr> eventDispatcher_; + /** + * Hold onto ContextContainer. See SchedulerToolbox. + * Must not be nullptr. + */ + ContextContainer::Shared contextContainer_; + /* * Temporary flags. */ - bool enableReparentingDetection_{false}; bool removeOutstandingSurfacesOnDestruction_{false}; }; diff --git a/android/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h b/android/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h index 33be7dcb6e3d1..cfd41c5d30d18 100644 --- a/android/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h +++ b/android/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h @@ -34,26 +34,32 @@ class SchedulerDelegate { */ virtual void schedulerDidRequestPreliminaryViewAllocation( SurfaceId surfaceId, - const ShadowView &shadowView) = 0; + const ShadowNode &shadowView) = 0; + + /* + * Called right after a ShadowNode is cloned. + */ + virtual void schedulerDidCloneShadowNode( + SurfaceId surfaceId, + const ShadowNode &oldShadowNode, + const ShadowNode &newShadowNode) = 0; virtual void schedulerDidDispatchCommand( const ShadowView &shadowView, std::string const &commandName, folly::dynamic const args) = 0; - /* - * Set JS responder for a view - */ - virtual void schedulerDidSetJSResponder( - SurfaceId surfaceId, + virtual void schedulerDidSendAccessibilityEvent( const ShadowView &shadowView, - const ShadowView &initialShadowView, - bool blockNativeResponder) = 0; + std::string const &eventType) = 0; /* - * Clear the JSResponder for a view + * Set JS responder for a view */ - virtual void schedulerDidClearJSResponder() = 0; + virtual void schedulerDidSetIsJSResponder( + ShadowView const &shadowView, + bool isJSResponder, + bool blockNativeResponder) = 0; virtual ~SchedulerDelegate() noexcept = default; }; diff --git a/android/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h b/android/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h index a98d450f37323..c6c2c179e0e7f 100644 --- a/android/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h +++ b/android/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h @@ -7,9 +7,14 @@ #pragma once +#include + #include #include #include +#include +#include +#include #include #include #include @@ -60,6 +65,11 @@ struct SchedulerToolbox final { * the call back synchronously if the executor is invoked on the main thread. */ BackgroundExecutor backgroundExecutor; + + /* + * A list of `UIManagerCommitHook`s that should be registered in `UIManager`. + */ + std::vector> commitHooks; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp b/android/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp new file mode 100644 index 0000000000000..9e36696803347 --- /dev/null +++ b/android/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp @@ -0,0 +1,317 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "SurfaceHandler.h" + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +using Status = SurfaceHandler::Status; + +SurfaceHandler::SurfaceHandler( + std::string const &moduleName, + SurfaceId surfaceId) noexcept { + parameters_.moduleName = moduleName; + parameters_.surfaceId = surfaceId; +} + +SurfaceHandler::SurfaceHandler(SurfaceHandler &&other) noexcept { + operator=(std::move(other)); +} + +SurfaceHandler &SurfaceHandler::operator=(SurfaceHandler &&other) noexcept { + std::unique_lock lock1(linkMutex_, std::defer_lock); + std::unique_lock lock2( + parametersMutex_, std::defer_lock); + std::unique_lock lock3( + other.linkMutex_, std::defer_lock); + std::unique_lock lock4( + other.parametersMutex_, std::defer_lock); + std::lock(lock1, lock2, lock3, lock4); + + link_ = other.link_; + parameters_ = other.parameters_; + + other.link_ = Link{}; + other.parameters_ = Parameters{}; + other.parameters_.contextContainer = parameters_.contextContainer; + return *this; +} + +#pragma mark - Surface Life-Cycle Management + +void SurfaceHandler::setContextContainer( + ContextContainer::Shared contextContainer) const noexcept { + parameters_.contextContainer = contextContainer; +} + +Status SurfaceHandler::getStatus() const noexcept { + std::shared_lock lock(linkMutex_); + return link_.status; +} + +void SurfaceHandler::start() const noexcept { + SystraceSection s("SurfaceHandler::start"); + std::unique_lock lock(linkMutex_); + react_native_assert( + link_.status == Status::Registered && "Surface must be registered."); + react_native_assert( + getLayoutConstraints().layoutDirection != LayoutDirection::Undefined && + "layoutDirection must be set."); + react_native_assert( + parameters_.contextContainer && "ContextContainer must be set."); + + auto parameters = Parameters{}; + { + SystraceSection s2("SurfaceHandler::start::paramsLock"); + std::shared_lock parametersLock(parametersMutex_); + parameters = parameters_; + } + + auto shadowTree = std::make_unique( + parameters.surfaceId, + parameters.layoutConstraints, + parameters.layoutContext, + *link_.uiManager, + *parameters.contextContainer); + + link_.shadowTree = shadowTree.get(); + + link_.uiManager->startSurface( + std::move(shadowTree), + parameters.moduleName, + parameters.props, + parameters_.displayMode); + + link_.status = Status::Running; + + applyDisplayMode(parameters.displayMode); +} + +void SurfaceHandler::stop() const noexcept { + auto shadowTree = ShadowTree::Unique{}; + { + std::unique_lock lock(linkMutex_); + react_native_assert( + link_.status == Status::Running && "Surface must be running."); + + link_.status = Status::Registered; + link_.shadowTree = nullptr; + shadowTree = link_.uiManager->stopSurface(parameters_.surfaceId); + } + + // As part of stopping a Surface, we need to properly destroy all + // mounted views, so we need to commit an empty tree to trigger all + // side-effects (including destroying and removing mounted views). + react_native_assert(shadowTree && "`shadowTree` must not be null."); + shadowTree->commitEmptyTree(); +} + +void SurfaceHandler::setDisplayMode(DisplayMode displayMode) const noexcept { + { + std::unique_lock lock(parametersMutex_); + if (parameters_.displayMode == displayMode) { + return; + } + + parameters_.displayMode = displayMode; + } + + { + std::shared_lock lock(linkMutex_); + + if (link_.status != Status::Running) { + return; + } + + link_.uiManager->setSurfaceProps( + parameters_.surfaceId, + parameters_.moduleName, + parameters_.props, + parameters_.displayMode); + + applyDisplayMode(displayMode); + } +} + +DisplayMode SurfaceHandler::getDisplayMode() const noexcept { + std::shared_lock lock(parametersMutex_); + return parameters_.displayMode; +} + +#pragma mark - Accessors + +SurfaceId SurfaceHandler::getSurfaceId() const noexcept { + std::shared_lock lock(parametersMutex_); + return parameters_.surfaceId; +} + +void SurfaceHandler::setSurfaceId(SurfaceId surfaceId) const noexcept { + std::unique_lock lock(parametersMutex_); + parameters_.surfaceId = surfaceId; +} + +std::string SurfaceHandler::getModuleName() const noexcept { + std::shared_lock lock(parametersMutex_); + return parameters_.moduleName; +} + +void SurfaceHandler::setProps(folly::dynamic const &props) const noexcept { + SystraceSection s("SurfaceHandler::setProps"); + std::unique_lock lock(parametersMutex_); + parameters_.props = props; +} + +folly::dynamic SurfaceHandler::getProps() const noexcept { + std::shared_lock lock(parametersMutex_); + return parameters_.props; +} + +std::shared_ptr +SurfaceHandler::getMountingCoordinator() const noexcept { + std::shared_lock lock(linkMutex_); + react_native_assert( + link_.status != Status::Unregistered && "Surface must be registered."); + react_native_assert( + link_.shadowTree && "`link_.shadowTree` must not be null."); + return link_.shadowTree->getMountingCoordinator(); +} + +#pragma mark - Layout + +Size SurfaceHandler::measure( + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept { + std::shared_lock lock(linkMutex_); + + if (link_.status != Status::Running) { + return layoutConstraints.clamp({0, 0}); + } + + react_native_assert( + link_.shadowTree && "`link_.shadowTree` must not be null."); + + auto currentRootShadowNode = + link_.shadowTree->getCurrentRevision().rootShadowNode; + + PropsParserContext propsParserContext{ + parameters_.surfaceId, *parameters_.contextContainer.get()}; + + auto rootShadowNode = currentRootShadowNode->clone( + propsParserContext, layoutConstraints, layoutContext); + rootShadowNode->layoutIfNeeded(); + return rootShadowNode->getLayoutMetrics().frame.size; +} + +void SurfaceHandler::constraintLayout( + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept { + SystraceSection s("SurfaceHandler::constraintLayout"); + { + std::unique_lock lock(parametersMutex_); + + if (parameters_.layoutConstraints == layoutConstraints && + parameters_.layoutContext == layoutContext) { + return; + } + + parameters_.layoutConstraints = layoutConstraints; + parameters_.layoutContext = layoutContext; + } + + { + std::shared_lock lock(linkMutex_); + + if (link_.status != Status::Running) { + return; + } + + PropsParserContext propsParserContext{ + parameters_.surfaceId, *parameters_.contextContainer.get()}; + + react_native_assert( + link_.shadowTree && "`link_.shadowTree` must not be null."); + link_.shadowTree->commit([&](RootShadowNode const &oldRootShadowNode) { + return oldRootShadowNode.clone( + propsParserContext, layoutConstraints, layoutContext); + }); + } +} + +LayoutConstraints SurfaceHandler::getLayoutConstraints() const noexcept { + std::shared_lock lock(parametersMutex_); + return parameters_.layoutConstraints; +} + +LayoutContext SurfaceHandler::getLayoutContext() const noexcept { + std::shared_lock lock(parametersMutex_); + return parameters_.layoutContext; +} + +#pragma mark - Private + +void SurfaceHandler::applyDisplayMode(DisplayMode displayMode) const noexcept { + SystraceSection s("SurfaceHandler::applyDisplayMode"); + react_native_assert( + link_.status == Status::Running && "Surface must be running."); + react_native_assert( + link_.shadowTree && "`link_.shadowTree` must not be null."); + + switch (displayMode) { + case DisplayMode::Visible: + link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal); + break; + case DisplayMode::Suspended: + link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended); + break; + case DisplayMode::Hidden: + link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal); + // Getting a current revision. + auto revision = link_.shadowTree->getCurrentRevision(); + // Committing an empty tree to force mounting to disassemble view + // hierarchy. + link_.shadowTree->commitEmptyTree(); + link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended); + // Committing the current revision back. It will be mounted only when + // `DisplayMode` is changed back to `Normal`. + link_.shadowTree->commit([&](RootShadowNode const &oldRootShadowNode) { + return std::static_pointer_cast( + revision.rootShadowNode->ShadowNode::clone(ShadowNodeFragment{})); + }); + break; + } +} + +void SurfaceHandler::setUIManager(UIManager const *uiManager) const noexcept { + std::unique_lock lock(linkMutex_); + + react_native_assert( + link_.status != Status::Running && "Surface must not be running."); + + if (link_.uiManager == uiManager) { + return; + } + + link_.uiManager = uiManager; + link_.status = uiManager ? Status::Registered : Status::Unregistered; +} + +SurfaceHandler::~SurfaceHandler() noexcept { + // TODO(T88046056): Fix Android memory leak before uncommenting changes + // react_native_assert( + // link_.status == Status::Unregistered && + // "`SurfaceHandler` must be unregistered (or moved-from) before + // deallocation."); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/scheduler/SurfaceHandler.h b/android/ReactCommon/react/renderer/scheduler/SurfaceHandler.h new file mode 100644 index 0000000000000..c0ae0e60bdc79 --- /dev/null +++ b/android/ReactCommon/react/renderer/scheduler/SurfaceHandler.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class Scheduler; +class ShadowTree; +class MountingCoordinator; +class UIManager; + +/* + * Represents a running React Native surface and provides control over it. + * The instances of this class are movable only. + * The instances of this class can be safely deallocated only if `status` is + * `Unregistered`; this is a way to enforce internal consistency and + * deallocation ordering constraints the core relies on. + * + * + * Even though all methods of the class are thread-safe, the consumer side must + * ensure the logical consistency of some methods (e.g. calling `stop` for + * non-running surface will crash). + */ +class SurfaceHandler { + public: + /* + * Represents a status of the `SurfaceHandler` instance. + */ + enum class Status { + /* + * Newly created, moved-from, or already-unregistered instances. The only + * state in which the object can be safely deallocated. + */ + Unregistered = 0, + + /* + * Registered instances that have an internal reference to a `UIManager` + * instance and ready to start a surface. + */ + Registered = 1, + + /* + * Registered and running instances. + */ + Running = 2, + }; + + /* + * Can be constructed anytime with a `moduleName` and a `surfaceId`. + */ + SurfaceHandler(std::string const &moduleName, SurfaceId surfaceId) noexcept; + virtual ~SurfaceHandler() noexcept; + + /* + * Movable-only. + */ + SurfaceHandler(SurfaceHandler &&SurfaceHandler) noexcept; + SurfaceHandler(SurfaceHandler const &SurfaceHandler) noexcept = delete; + SurfaceHandler &operator=(SurfaceHandler &&other) noexcept; + SurfaceHandler &operator=(SurfaceHandler const &other) noexcept = delete; + +#pragma mark - Surface Life-Cycle Management + + /* + * Must be called before surface is started. + */ + void setContextContainer( + ContextContainer::Shared contextContainer) const noexcept; + + /* + * Returns a momentum value of the status. + */ + Status getStatus() const noexcept; + + /* + * Starts or stops the surface. + * Can not be called when the status is `Unregistered`. + * `start()` must not be called for a running surface, and `stop()` must not + * be called for a not running surface. + */ + void start() const noexcept; + void stop() const noexcept; + + /* + * Sets (and gets) the running mode. + * The running mode can be changed anytime (even for `Unregistered` surface). + */ + virtual void setDisplayMode(DisplayMode displayMode) const noexcept; + DisplayMode getDisplayMode() const noexcept; + +#pragma mark - Accessors + + SurfaceId getSurfaceId() const noexcept; + void setSurfaceId(SurfaceId surfaceId) const noexcept; + std::string getModuleName() const noexcept; + + /* + * Provides access for surface props. + * Props can be changed anytime (even for `Unregistered` surface). + */ + void setProps(folly::dynamic const &props) const noexcept; + folly::dynamic getProps() const noexcept; + + /* + * Returns a `MountingCoordinator` instance associated with a running surface. + * Can be not be called when the status is `Unregistered`. + * The returning value cannot be `nullptr`. + */ + std::shared_ptr getMountingCoordinator() + const noexcept; + +#pragma mark - Layout + + /* + * Measures the surface with given layout constraints and layout context. + * Returns zero size if called on the stopped or unregistered surface. + */ + Size measure( + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept; + + /* + * Sets layout constraints and layout context for the surface. + */ + void constraintLayout( + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept; + + /* + * Returns layout constraints and layout context associated with the surface. + */ + LayoutConstraints getLayoutConstraints() const noexcept; + LayoutContext getLayoutContext() const noexcept; + + private: + friend class Scheduler; + + /* + * Must be called by `Scheduler` during registration process. + */ + void setUIManager(UIManager const *uiManager) const noexcept; + + void applyDisplayMode(DisplayMode displayMode) const noexcept; + +#pragma mark - Link & Parameters + + /* + * All data members of the class are split into two groups (`Link` and + * `Parameters`) that require separate synchronization. This way it's easier + * to see that proper lock is acquired. Separate synchronization is needed to + * prevent deadlocks. + */ + + /* + * Represents parameters of the surface. Parameters can be changed + * independently from controlling the running state + * (registering/unregistering, starting/stopping) of the surface. + * Changing parameters requires acquiring a unique lock; reading needs only + * a shared lock. + */ + struct Parameters { + std::string moduleName{}; + SurfaceId surfaceId{}; + DisplayMode displayMode{DisplayMode::Visible}; + folly::dynamic props{}; + LayoutConstraints layoutConstraints{}; + LayoutContext layoutContext{}; + ContextContainer::Shared contextContainer{}; + }; + + /* + * Represents an underlying link to a `ShadowTree` and an `UIMananger`. + * Registering, unregistering, starting, and stopping the surface requires + * acquiring a unique lock; other access needs only a shared lock. + */ + struct Link { + Status status{Status::Unregistered}; + UIManager const *uiManager{}; + ShadowTree const *shadowTree{}; + }; + + /* + * `link_` and `linkMutex_` pair. + */ + mutable better::shared_mutex linkMutex_; + mutable Link link_; + + /* + * `parameters_` and `parametersMutex_` pair. + */ + mutable better::shared_mutex parametersMutex_; + mutable Parameters parameters_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/scheduler/SurfaceManager.cpp b/android/ReactCommon/react/renderer/scheduler/SurfaceManager.cpp new file mode 100644 index 0000000000000..a6f8a537f92c5 --- /dev/null +++ b/android/ReactCommon/react/renderer/scheduler/SurfaceManager.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "SurfaceManager.h" + +#include + +namespace facebook { +namespace react { + +SurfaceManager::SurfaceManager(Scheduler const &scheduler) noexcept + : scheduler_(scheduler) {} + +void SurfaceManager::startSurface( + SurfaceId surfaceId, + std::string const &moduleName, + folly::dynamic const &props, + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept { + { + std::unique_lock lock(mutex_); + auto surfaceHandler = SurfaceHandler{moduleName, surfaceId}; + surfaceHandler.setContextContainer(scheduler_.getContextContainer()); + registry_.emplace(surfaceId, std::move(surfaceHandler)); + } + + visit(surfaceId, [&](SurfaceHandler const &surfaceHandler) { + surfaceHandler.setProps(props); + surfaceHandler.constraintLayout(layoutConstraints, layoutContext); + + scheduler_.registerSurface(surfaceHandler); + + surfaceHandler.start(); + }); +} + +void SurfaceManager::stopSurface(SurfaceId surfaceId) const noexcept { + visit(surfaceId, [&](SurfaceHandler const &surfaceHandler) { + surfaceHandler.stop(); + scheduler_.unregisterSurface(surfaceHandler); + }); + + { + std::unique_lock lock(mutex_); + + auto iterator = registry_.find(surfaceId); + registry_.erase(iterator); + } +} + +Size SurfaceManager::measureSurface( + SurfaceId surfaceId, + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept { + auto size = Size{}; + + visit(surfaceId, [&](SurfaceHandler const &surfaceHandler) { + size = surfaceHandler.measure(layoutConstraints, layoutContext); + }); + + return size; +} + +MountingCoordinator::Shared SurfaceManager::findMountingCoordinator( + SurfaceId surfaceId) const noexcept { + auto mountingCoordinator = MountingCoordinator::Shared{}; + + visit(surfaceId, [&](SurfaceHandler const &surfaceHandler) { + mountingCoordinator = surfaceHandler.getMountingCoordinator(); + }); + + return mountingCoordinator; +} + +void SurfaceManager::constraintSurfaceLayout( + SurfaceId surfaceId, + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept { + visit(surfaceId, [=](SurfaceHandler const &surfaceHandler) { + surfaceHandler.constraintLayout(layoutConstraints, layoutContext); + }); +} + +void SurfaceManager::visit( + SurfaceId surfaceId, + std::function callback) + const noexcept { + std::shared_lock lock(mutex_); + + auto iterator = registry_.find(surfaceId); + + if (iterator == registry_.end()) { + return; + } + + callback(iterator->second); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/scheduler/SurfaceManager.h b/android/ReactCommon/react/renderer/scheduler/SurfaceManager.h new file mode 100644 index 0000000000000..83052f1855d77 --- /dev/null +++ b/android/ReactCommon/react/renderer/scheduler/SurfaceManager.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * `SurfaceManager` allows controlling React Native Surfaces via + * `SurfaceHandler` without using `SurfaceHandler` directly. `SurfaceManager` + * maintains a registry of `SurfaceHandler`s and allows to reference to them via + * a `SurfaceId`. + * The is supposed to be used during the transition period only. + */ +class SurfaceManager final { + public: + SurfaceManager(Scheduler const &scheduler) noexcept; + +#pragma mark - Surface Management + + void startSurface( + SurfaceId surfaceId, + std::string const &moduleName, + folly::dynamic const &props, + LayoutConstraints const &layoutConstraints = {}, + LayoutContext const &layoutContext = {}) const noexcept; + + void stopSurface(SurfaceId surfaceId) const noexcept; + + Size measureSurface( + SurfaceId surfaceId, + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept; + + void constraintSurfaceLayout( + SurfaceId surfaceId, + LayoutConstraints const &layoutConstraints, + LayoutContext const &layoutContext) const noexcept; + + MountingCoordinator::Shared findMountingCoordinator( + SurfaceId surfaceId) const noexcept; + + private: + void visit( + SurfaceId surfaceId, + std::function callback) + const noexcept; + + Scheduler const &scheduler_; + mutable better::shared_mutex mutex_; // Protects `registry_`. + mutable better::map registry_{}; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.cpp b/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.cpp index 245e07b463bcd..66d6c8c8a0f5f 100644 --- a/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.cpp +++ b/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.cpp @@ -7,15 +7,19 @@ #include "SynchronousEventBeat.h" +#include + namespace facebook { namespace react { SynchronousEventBeat::SynchronousEventBeat( RunLoopObserver::Unique uiRunLoopObserver, - RuntimeExecutor runtimeExecutor) + RuntimeExecutor runtimeExecutor, + std::shared_ptr const &runtimeScheduler) : EventBeat({}), uiRunLoopObserver_(std::move(uiRunLoopObserver)), - runtimeExecutor_(std::move(runtimeExecutor)) { + runtimeExecutor_(std::move(runtimeExecutor)), + runtimeScheduler_(runtimeScheduler) { uiRunLoopObserver_->setDelegate(this); uiRunLoopObserver_->enable(); } @@ -23,7 +27,7 @@ SynchronousEventBeat::SynchronousEventBeat( void SynchronousEventBeat::activityDidChange( RunLoopObserver::Delegate const *delegate, RunLoopObserver::Activity activity) const noexcept { - assert(delegate == this); + react_native_assert(delegate == this); lockExecutorAndBeat(); } @@ -42,8 +46,13 @@ void SynchronousEventBeat::lockExecutorAndBeat() const { return; } - executeSynchronouslyOnSameThread_CAN_DEADLOCK( - runtimeExecutor_, [this](jsi::Runtime &runtime) { beat(runtime); }); + if (runtimeScheduler_) { + runtimeScheduler_->executeNowOnTheSameThread( + [this](jsi::Runtime &runtime) { beat(runtime); }); + } else { + executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor_, [this](jsi::Runtime &runtime) { beat(runtime); }); + } } } // namespace react diff --git a/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.h b/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.h index 109a6ddf0b625..a3e2d6489d18d 100644 --- a/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.h +++ b/android/ReactCommon/react/renderer/scheduler/SynchronousEventBeat.h @@ -9,6 +9,7 @@ #include #include +#include #include namespace facebook { @@ -23,7 +24,8 @@ class SynchronousEventBeat final : public EventBeat, public: SynchronousEventBeat( RunLoopObserver::Unique uiRunLoopObserver, - RuntimeExecutor runtimeExecutor); + RuntimeExecutor runtimeExecutor, + std::shared_ptr const &runtimeScheduler); void induce() const override; @@ -38,6 +40,7 @@ class SynchronousEventBeat final : public EventBeat, RunLoopObserver::Unique uiRunLoopObserver_; RuntimeExecutor runtimeExecutor_; + std::shared_ptr runtimeScheduler_; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/telemetry/Android.mk b/android/ReactCommon/react/renderer/telemetry/Android.mk new file mode 100644 index 0000000000000..990ec538ff5fc --- /dev/null +++ b/android/ReactCommon/react/renderer/telemetry/Android.mk @@ -0,0 +1,31 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_render_telemetry + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"Fabric\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +LOCAL_STATIC_LIBRARIES := + +LOCAL_SHARED_LIBRARIES := libbetter libyoga libfolly_futures glog libfolly_json libglog_init libreact_render_core libreact_render_debug librrc_view librrc_root libreact_utils libreact_debug + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,better) +$(call import-module,glog) +$(call import-module,folly) +$(call import-module,react/utils) diff --git a/android/ReactCommon/react/renderer/telemetry/BUCK b/android/ReactCommon/react/renderer/telemetry/BUCK new file mode 100644 index 0000000000000..d6b46e35dec66 --- /dev/null +++ b/android/ReactCommon/react/renderer/telemetry/BUCK @@ -0,0 +1,78 @@ +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "fb_xplat_cxx_test", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "telemetry", + srcs = glob( + ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), + ), + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ("stubs", "*.h"), + ], + prefix = "react/renderer/telemetry", + ), + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + tests = [":tests"], + visibility = ["PUBLIC"], + deps = [ + "//third-party/glog:glog", + "//xplat/fbsystrace:fbsystrace", + "//xplat/folly:headers_only", + "//xplat/folly:memory", + "//xplat/folly:molly", + react_native_xplat_target("better:better"), + react_native_xplat_target("react/debug:debug"), + react_native_xplat_target("react/utils:utils"), + ], +) + +fb_xplat_cxx_test( + name = "tests", + srcs = glob(["tests/**/*.cpp"]), + headers = glob(["tests/**/*.h"]), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++17", + "-Wall", + ], + contacts = ["oncall+react_native@xmail.facebook.com"], + fbandroid_use_instrumentation_test = True, + platforms = (ANDROID, APPLE, CXX), + deps = [ + ":telemetry", + "//xplat/folly:molly", + "//xplat/third-party/gmock:gtest", + react_native_xplat_target("react/test_utils:test_utils"), + ], +) diff --git a/android/ReactCommon/react/renderer/mounting/SurfaceTelemetry.cpp b/android/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.cpp similarity index 93% rename from android/ReactCommon/react/renderer/mounting/SurfaceTelemetry.cpp rename to android/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.cpp index 6b8a8047bdb6b..5d836b7006ea3 100644 --- a/android/ReactCommon/react/renderer/mounting/SurfaceTelemetry.cpp +++ b/android/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.cpp @@ -16,6 +16,7 @@ void SurfaceTelemetry::incorporate( TransactionTelemetry const &telemetry, int numberOfMutations) { layoutTime_ += telemetry.getLayoutEndTime() - telemetry.getLayoutStartTime(); + textMeasureTime_ += telemetry.getTextMeasureTime(); commitTime_ += telemetry.getCommitEndTime() - telemetry.getCommitStartTime(); diffTime_ += telemetry.getDiffEndTime() - telemetry.getDiffStartTime(); mountTime_ += telemetry.getMountEndTime() - telemetry.getMountStartTime(); @@ -37,6 +38,10 @@ TelemetryDuration SurfaceTelemetry::getLayoutTime() const { return layoutTime_; } +TelemetryDuration SurfaceTelemetry::getTextMeasureTime() const { + return textMeasureTime_; +} + TelemetryDuration SurfaceTelemetry::getCommitTime() const { return commitTime_; } diff --git a/android/ReactCommon/react/renderer/mounting/SurfaceTelemetry.h b/android/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h similarity index 91% rename from android/ReactCommon/react/renderer/mounting/SurfaceTelemetry.h rename to android/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h index 02c8fbe964659..6a72636120002 100644 --- a/android/ReactCommon/react/renderer/mounting/SurfaceTelemetry.h +++ b/android/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h @@ -10,7 +10,7 @@ #include #include -#include +#include #include namespace facebook { @@ -28,6 +28,7 @@ class SurfaceTelemetry final { * Metrics */ TelemetryDuration getLayoutTime() const; + TelemetryDuration getTextMeasureTime() const; TelemetryDuration getCommitTime() const; TelemetryDuration getDiffTime() const; TelemetryDuration getMountTime() const; @@ -50,6 +51,7 @@ class SurfaceTelemetry final { private: TelemetryDuration layoutTime_{}; TelemetryDuration commitTime_{}; + TelemetryDuration textMeasureTime_{}; TelemetryDuration diffTime_{}; TelemetryDuration mountTime_{}; diff --git a/android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.cpp b/android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.cpp new file mode 100644 index 0000000000000..b7b5fe37c033d --- /dev/null +++ b/android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "TransactionTelemetry.h" + +#include + +namespace facebook { +namespace react { + +thread_local TransactionTelemetry *threadLocalTransactionTelemetry = nullptr; + +TransactionTelemetry::TransactionTelemetry() + : TransactionTelemetry(telemetryTimePointNow) {} + +TransactionTelemetry::TransactionTelemetry( + std::function now) + : now_{now} {} + +TransactionTelemetry *TransactionTelemetry::threadLocalTelemetry() { + return threadLocalTransactionTelemetry; +} + +void TransactionTelemetry::setAsThreadLocal() { + threadLocalTransactionTelemetry = this; +} + +void TransactionTelemetry::unsetAsThreadLocal() { + threadLocalTransactionTelemetry = nullptr; +} + +void TransactionTelemetry::willCommit() { + react_native_assert(commitStartTime_ == kTelemetryUndefinedTimePoint); + react_native_assert(commitEndTime_ == kTelemetryUndefinedTimePoint); + commitStartTime_ = now_(); +} + +void TransactionTelemetry::didCommit() { + react_native_assert(commitStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(commitEndTime_ == kTelemetryUndefinedTimePoint); + commitEndTime_ = now_(); +} + +void TransactionTelemetry::willDiff() { + react_native_assert(diffStartTime_ == kTelemetryUndefinedTimePoint); + react_native_assert(diffEndTime_ == kTelemetryUndefinedTimePoint); + diffStartTime_ = now_(); +} + +void TransactionTelemetry::didDiff() { + react_native_assert(diffStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(diffEndTime_ == kTelemetryUndefinedTimePoint); + diffEndTime_ = now_(); +} + +void TransactionTelemetry::willLayout() { + react_native_assert(layoutStartTime_ == kTelemetryUndefinedTimePoint); + react_native_assert(layoutEndTime_ == kTelemetryUndefinedTimePoint); + layoutStartTime_ = now_(); +} + +void TransactionTelemetry::willMeasureText() { + react_native_assert( + lastTextMeasureStartTime_ == kTelemetryUndefinedTimePoint); + lastTextMeasureStartTime_ = now_(); +} + +void TransactionTelemetry::didMeasureText() { + numberOfTextMeasurements_++; + react_native_assert( + lastTextMeasureStartTime_ != kTelemetryUndefinedTimePoint); + textMeasureTime_ += now_() - lastTextMeasureStartTime_; + lastTextMeasureStartTime_ = kTelemetryUndefinedTimePoint; +} + +void TransactionTelemetry::didLayout() { + react_native_assert(layoutStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(layoutEndTime_ == kTelemetryUndefinedTimePoint); + layoutEndTime_ = now_(); +} + +void TransactionTelemetry::willMount() { + react_native_assert(mountStartTime_ == kTelemetryUndefinedTimePoint); + react_native_assert(mountEndTime_ == kTelemetryUndefinedTimePoint); + mountStartTime_ = now_(); +} + +void TransactionTelemetry::didMount() { + react_native_assert(mountStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(mountEndTime_ == kTelemetryUndefinedTimePoint); + mountEndTime_ = now_(); +} + +void TransactionTelemetry::setRevisionNumber(int revisionNumber) { + revisionNumber_ = revisionNumber; +} + +TelemetryTimePoint TransactionTelemetry::getDiffStartTime() const { + react_native_assert(diffStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(diffEndTime_ != kTelemetryUndefinedTimePoint); + return diffStartTime_; +} + +TelemetryTimePoint TransactionTelemetry::getDiffEndTime() const { + react_native_assert(diffStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(diffEndTime_ != kTelemetryUndefinedTimePoint); + return diffEndTime_; +} + +TelemetryTimePoint TransactionTelemetry::getCommitStartTime() const { + react_native_assert(commitStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(commitEndTime_ != kTelemetryUndefinedTimePoint); + return commitStartTime_; +} + +TelemetryTimePoint TransactionTelemetry::getCommitEndTime() const { + react_native_assert(commitStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(commitEndTime_ != kTelemetryUndefinedTimePoint); + return commitEndTime_; +} + +TelemetryTimePoint TransactionTelemetry::getLayoutStartTime() const { + react_native_assert(layoutStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(layoutEndTime_ != kTelemetryUndefinedTimePoint); + return layoutStartTime_; +} + +TelemetryTimePoint TransactionTelemetry::getLayoutEndTime() const { + react_native_assert(layoutStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(layoutEndTime_ != kTelemetryUndefinedTimePoint); + return layoutEndTime_; +} + +TelemetryTimePoint TransactionTelemetry::getMountStartTime() const { + react_native_assert(mountStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(mountEndTime_ != kTelemetryUndefinedTimePoint); + return mountStartTime_; +} + +TelemetryTimePoint TransactionTelemetry::getMountEndTime() const { + react_native_assert(mountStartTime_ != kTelemetryUndefinedTimePoint); + react_native_assert(mountEndTime_ != kTelemetryUndefinedTimePoint); + return mountEndTime_; +} + +TelemetryDuration TransactionTelemetry::getTextMeasureTime() const { + return textMeasureTime_; +} + +int TransactionTelemetry::getNumberOfTextMeasurements() const { + return numberOfTextMeasurements_; +} + +int TransactionTelemetry::getRevisionNumber() const { + return revisionNumber_; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/mounting/TransactionTelemetry.h b/android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h similarity index 85% rename from android/ReactCommon/react/renderer/mounting/TransactionTelemetry.h rename to android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h index 88ef2a23b0e3c..ab3a398d78537 100644 --- a/android/ReactCommon/react/renderer/mounting/TransactionTelemetry.h +++ b/android/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -25,6 +26,10 @@ class TransactionTelemetry final { * Thread-local Telemetry instance */ static TransactionTelemetry *threadLocalTelemetry(); + + TransactionTelemetry(); + TransactionTelemetry(std::function now); + void setAsThreadLocal(); void unsetAsThreadLocal(); @@ -36,6 +41,7 @@ class TransactionTelemetry final { void willCommit(); void didCommit(); void willLayout(); + void willMeasureText(); void didMeasureText(); void didLayout(); void willMount(); @@ -55,6 +61,7 @@ class TransactionTelemetry final { TelemetryTimePoint getMountStartTime() const; TelemetryTimePoint getMountEndTime() const; + TelemetryDuration getTextMeasureTime() const; int getNumberOfTextMeasurements() const; int getRevisionNumber() const; @@ -68,8 +75,12 @@ class TransactionTelemetry final { TelemetryTimePoint mountStartTime_{kTelemetryUndefinedTimePoint}; TelemetryTimePoint mountEndTime_{kTelemetryUndefinedTimePoint}; + TelemetryTimePoint lastTextMeasureStartTime_{kTelemetryUndefinedTimePoint}; + TelemetryDuration textMeasureTime_{0}; + int numberOfTextMeasurements_{0}; int revisionNumber_{0}; + std::function now_; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/telemetry/tests/TransactionTelemetryTest.cpp b/android/ReactCommon/react/renderer/telemetry/tests/TransactionTelemetryTest.cpp new file mode 100644 index 0000000000000..95c6dfcf1b078 --- /dev/null +++ b/android/ReactCommon/react/renderer/telemetry/tests/TransactionTelemetryTest.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include + +#include +#include +#include + +using namespace facebook::react; + +MockClock::time_point MockClock::time_ = {}; + +/** + * Ensures that the at least the specified time passes on a real clock. + * Why at least? Because operating systems provide no guarantee that our thread + * gets processing time after the specified time. What about using a busywait? + * Busywait are also affected by the non-deterministic OS process scheduling. + * The OS might decide right before the specified time elapsed to schedule + * another thread/process, with the result that more time passes in reality than + * the caller intended. Prefer the `MockClock` and only use this function to + * verify that at least the specifid time has passed but wihtout making exact + * verifications. + */ +static void sleepAtLeast(double durationInSeconds) { + std::this_thread::sleep_for( + std::chrono::milliseconds((long long)(durationInSeconds * 1000))); +} + +TEST(TransactionTelemetryTest, timepoints) { + auto timepointA = telemetryTimePointNow(); + sleepAtLeast(0.1); + auto timepointB = telemetryTimePointNow(); + + auto duration = telemetryDurationToMilliseconds(timepointB - timepointA); + + EXPECT_GE(duration, 100); +} + +TEST(TransactionTelemetryTest, normalUseCase) { + auto telemetry = TransactionTelemetry{[]() { return MockClock::now(); }}; + + telemetry.setAsThreadLocal(); + + telemetry.willCommit(); + MockClock::advance_by(std::chrono::milliseconds(100)); + telemetry.willLayout(); + MockClock::advance_by(std::chrono::milliseconds(200)); + + TransactionTelemetry::threadLocalTelemetry()->willMeasureText(); + MockClock::advance_by(std::chrono::milliseconds(100)); + TransactionTelemetry::threadLocalTelemetry()->didMeasureText(); + + TransactionTelemetry::threadLocalTelemetry()->willMeasureText(); + MockClock::advance_by(std::chrono::milliseconds(200)); + TransactionTelemetry::threadLocalTelemetry()->didMeasureText(); + + TransactionTelemetry::threadLocalTelemetry()->willMeasureText(); + MockClock::advance_by(std::chrono::milliseconds(300)); + TransactionTelemetry::threadLocalTelemetry()->didMeasureText(); + + telemetry.didLayout(); + MockClock::advance_by(std::chrono::milliseconds(100)); + telemetry.didCommit(); + + telemetry.setRevisionNumber(42); + + telemetry.unsetAsThreadLocal(); + + MockClock::advance_by(std::chrono::milliseconds(300)); + + telemetry.willMount(); + MockClock::advance_by(std::chrono::milliseconds(100)); + telemetry.didMount(); + + auto commitDuration = telemetryDurationToMilliseconds( + telemetry.getCommitEndTime() - telemetry.getCommitStartTime()); + auto layoutDuration = telemetryDurationToMilliseconds( + telemetry.getLayoutEndTime() - telemetry.getLayoutStartTime()); + auto mountDuration = telemetryDurationToMilliseconds( + telemetry.getMountEndTime() - telemetry.getMountStartTime()); + + EXPECT_EQ(commitDuration, 1000); + EXPECT_EQ(layoutDuration, 800); + EXPECT_EQ(mountDuration, 100); + + EXPECT_EQ(telemetry.getNumberOfTextMeasurements(), 3); + EXPECT_EQ( + telemetryDurationToMilliseconds(telemetry.getTextMeasureTime()), 600); + EXPECT_EQ(telemetry.getRevisionNumber(), 42); +} + +TEST(TransactionTelemetryTest, defaultImplementation) { + auto telemetry = TransactionTelemetry{}; + + telemetry.setAsThreadLocal(); + + telemetry.willCommit(); + sleepAtLeast(0.1); + telemetry.willLayout(); + sleepAtLeast(0.2); + + TransactionTelemetry::threadLocalTelemetry()->willMeasureText(); + sleepAtLeast(0.1); + TransactionTelemetry::threadLocalTelemetry()->didMeasureText(); + + telemetry.didLayout(); + sleepAtLeast(0.1); + telemetry.didCommit(); + + telemetry.unsetAsThreadLocal(); + + telemetry.willMount(); + sleepAtLeast(0.1); + telemetry.didMount(); + + auto commitDuration = telemetryDurationToMilliseconds( + telemetry.getCommitEndTime() - telemetry.getCommitStartTime()); + auto layoutDuration = telemetryDurationToMilliseconds( + telemetry.getLayoutEndTime() - telemetry.getLayoutStartTime()); + auto mountDuration = telemetryDurationToMilliseconds( + telemetry.getMountEndTime() - telemetry.getMountStartTime()); + + EXPECT_GE(commitDuration, 500); + EXPECT_GE(layoutDuration, 300); + EXPECT_GE(mountDuration, 100); +} + +TEST(TransactionTelemetryTest, abnormalUseCases) { + // Calling `did` before `will` should crash. + EXPECT_DEATH_IF_SUPPORTED( + { + auto telemetry = TransactionTelemetry{}; + telemetry.didDiff(); + }, + "diffStartTime_"); + + EXPECT_DEATH_IF_SUPPORTED( + { + auto telemetry = TransactionTelemetry{}; + telemetry.didCommit(); + }, + "commitStartTime_"); + + EXPECT_DEATH_IF_SUPPORTED( + { + auto telemetry = TransactionTelemetry{}; + telemetry.didMount(); + }, + "mountStartTime_"); + + // Getting `start` *or* `end` timepoints before a pair of `will` and `did` + // should crash. + EXPECT_DEATH_IF_SUPPORTED( + { + auto telemetry = TransactionTelemetry{}; + telemetry.willCommit(); + telemetry.getCommitStartTime(); + }, + "commitEndTime_"); + + EXPECT_DEATH_IF_SUPPORTED( + { + auto telemetry = TransactionTelemetry{}; + telemetry.willCommit(); + telemetry.getCommitEndTime(); + }, + "commitEndTime_"); +} diff --git a/android/ReactCommon/react/renderer/templateprocessor/Android.mk b/android/ReactCommon/react/renderer/templateprocessor/Android.mk index b40bb579ddf66..23ebd6b9a606b 100644 --- a/android/ReactCommon/react/renderer/templateprocessor/Android.mk +++ b/android/ReactCommon/react/renderer/templateprocessor/Android.mk @@ -17,7 +17,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := diff --git a/android/ReactCommon/react/renderer/templateprocessor/BUCK b/android/ReactCommon/react/renderer/templateprocessor/BUCK index ea7ad4415ccc4..430b0bdb1d3b0 100644 --- a/android/ReactCommon/react/renderer/templateprocessor/BUCK +++ b/android/ReactCommon/react/renderer/templateprocessor/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -31,12 +31,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/templateprocessor", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -73,7 +67,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/templateprocessor/UITemplateProcessor.cpp b/android/ReactCommon/react/renderer/templateprocessor/UITemplateProcessor.cpp index 22d506efdcfda..d9f4e44d1dc8b 100644 --- a/android/ReactCommon/react/renderer/templateprocessor/UITemplateProcessor.cpp +++ b/android/ReactCommon/react/renderer/templateprocessor/UITemplateProcessor.cpp @@ -44,14 +44,14 @@ SharedShadowNode UITemplateProcessor::runCommand( const int tagOffset = 420000; // TODO: change to integer codes and a switch statement if (opcode == "createNode") { - int tag = command[1].asInt(); + Tag tag = static_cast(command[1].asInt()); const auto &type = command[2].asString(); const auto parentTag = command[3].asInt(); const auto &props = command[4]; nodes[tag] = componentDescriptorRegistry.createNode( tag + tagOffset, type, surfaceId, props, nullptr); if (parentTag > -1) { // parentTag == -1 indicates root node - auto parentShadowNode = nodes[parentTag]; + auto parentShadowNode = nodes[static_cast(parentTag)]; auto const &componentDescriptor = componentDescriptorRegistry.at( parentShadowNode->getComponentHandle()); componentDescriptor.appendChild(parentShadowNode, nodes[tag]); @@ -61,13 +61,13 @@ SharedShadowNode UITemplateProcessor::runCommand( LOG(INFO) << "(stop) UITemplateProcessor inject serialized 'server rendered' view tree"; } - return nodes[command[1].asInt()]; + return nodes[static_cast(command[1].asInt())]; } else if (opcode == "loadNativeBool") { - int registerNumber = command[1].asInt(); + auto registerNumber = static_cast(command[1].asInt()); std::string param = command[4][0].asString(); registers[registerNumber] = reactNativeConfig->getBool(param); } else if (opcode == "conditional") { - int registerNumber = command[1].asInt(); + auto registerNumber = static_cast(command[1].asInt()); auto conditionDynamic = registers[registerNumber]; if (conditionDynamic.isNull()) { // TODO: provide original command or command line? diff --git a/android/ReactCommon/react/renderer/templateprocessor/tests/UITemplateProcessorTest.cpp b/android/ReactCommon/react/renderer/templateprocessor/tests/UITemplateProcessorTest.cpp index 5c7220f52a44b..add08294321f0 100644 --- a/android/ReactCommon/react/renderer/templateprocessor/tests/UITemplateProcessorTest.cpp +++ b/android/ReactCommon/react/renderer/templateprocessor/tests/UITemplateProcessorTest.cpp @@ -89,8 +89,8 @@ TEST(UITemplateProcessorTest, testSimpleBytecode) { auto nativeModuleRegistry = buildNativeModuleRegistry(); auto bytecode = R"delim({"version":0.1,"commands":[ - ["createNode",2,"RCTView",-1,{"opacity": 0.5, "testId": "root"}], - ["createNode",4,"RCTView",2,{"testId": "child"}], + ["createNode",2,"RCTView",-1,{"opacity": 0.5, "testID": "root"}], + ["createNode",4,"RCTView",2,{"testID": "child"}], ["returnRoot",2] ]})delim"; @@ -124,11 +124,11 @@ TEST(UITemplateProcessorTest, testConditionalBytecode) { auto nativeModuleRegistry = buildNativeModuleRegistry(); auto bytecode = R"delim({"version":0.1,"commands":[ - ["createNode",2,"RCTView",-1,{"testId": "root"}], + ["createNode",2,"RCTView",-1,{"testID": "root"}], ["loadNativeBool",1,"MobileConfig","getBool",["qe:simple_test"]], ["conditional",1, - [["createNode",4,"RCTView",2,{"testId": "cond_true"}]], - [["createNode",4,"RCTView",2,{"testId": "cond_false"}]] + [["createNode",4,"RCTView",2,{"testID": "cond_true"}]], + [["createNode",4,"RCTView",2,{"testID": "cond_false"}]] ], ["returnRoot",2] ]})delim"; diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/Android.mk b/android/ReactCommon/react/renderer/textlayoutmanager/Android.mk index d048ec0006132..0d216e572762b 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/Android.mk +++ b/android/ReactCommon/react/renderer/textlayoutmanager/Android.mk @@ -11,7 +11,7 @@ LOCAL_MODULE := react_render_textlayoutmanager LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp $(LOCAL_PATH)/platform/android/react/renderer/textlayoutmanager/*.cpp) -LOCAL_SHARED_LIBRARIES := libfolly_futures libreactnativeutilsjni libreact_utils libfb libfbjni libreact_render_uimanager libreact_render_componentregistry libreact_render_attributedstring libfolly_json libyoga libfolly_json libreact_render_core libreact_render_debug libreact_render_graphics +LOCAL_SHARED_LIBRARIES := libfolly_futures libreactnativeutilsjni libreact_utils libfb libfbjni libreact_render_uimanager libreact_render_componentregistry libreact_render_attributedstring libreact_render_mounting glog libfolly_json libglog_init libyoga libreact_render_core libreact_render_debug libreact_render_graphics libreact_debug libreact_render_mapbuffer libmapbufferjni libreact_render_telemetry LOCAL_STATIC_LIBRARIES := @@ -22,7 +22,7 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/platform/androi LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_SHARED_LIBRARY) @@ -34,7 +34,10 @@ $(call import-module,react/renderer/componentregistry) $(call import-module,react/renderer/core) $(call import-module,react/renderer/attributedstring) $(call import-module,react/renderer/debug) +$(call import-module,react/renderer/mounting) $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/uimanager) $(call import-module,react/utils) $(call import-module,yogajni) +$(call import-module,react/renderer/mapbuffer) +$(call import-module,react/renderer/telemetry) diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/BUCK b/android/ReactCommon/react/renderer/textlayoutmanager/BUCK index 6b1fa35b9049d..1e7e142f4cb24 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/BUCK +++ b/android/ReactCommon/react/renderer/textlayoutmanager/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -8,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", @@ -36,12 +36,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/textlayoutmanager", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], cxx_exported_headers = subdir_glob( [ ("platform/cxx", "*.h"), @@ -62,6 +56,7 @@ rn_xplat_cxx_library( cxx_tests = [":tests"], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbandroid_exported_headers = subdir_glob( [ @@ -129,6 +124,7 @@ rn_xplat_cxx_library( react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/graphics:graphics"), react_native_xplat_target("react/renderer/uimanager:uimanager"), + react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), ], ) @@ -140,7 +136,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/android/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index d3d9845f93c51..76bbc45c96340 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/android/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -12,11 +12,11 @@ namespace react { static Rect rectFromDynamic(folly::dynamic const &data) { Point origin; - origin.x = data.getDefault("x", 0).getDouble(); - origin.y = data.getDefault("y", 0).getDouble(); + origin.x = static_cast(data.getDefault("x", 0).getDouble()); + origin.y = static_cast(data.getDefault("y", 0).getDouble()); Size size; - size.width = data.getDefault("width", 0).getDouble(); - size.height = data.getDefault("height", 0).getDouble(); + size.width = static_cast(data.getDefault("width", 0).getDouble()); + size.height = static_cast(data.getDefault("height", 0).getDouble()); Rect frame; frame.origin = origin; frame.size = size; @@ -34,15 +34,18 @@ LineMeasurement::LineMeasurement( frame(frame), descender(descender), capHeight(capHeight), - ascender(ascender) {} + ascender(ascender), + xHeight(xHeight) {} LineMeasurement::LineMeasurement(folly::dynamic const &data) : text(data.getDefault("text", "").getString()), frame(rectFromDynamic(data)), - descender(data.getDefault("descender", 0).getDouble()), - capHeight(data.getDefault("capHeight", 0).getDouble()), - ascender(data.getDefault("ascender", 0).getDouble()), - xHeight(data.getDefault("xHeight", 0).getDouble()) {} + descender( + static_cast(data.getDefault("descender", 0).getDouble())), + capHeight( + static_cast(data.getDefault("capHeight", 0).getDouble())), + ascender(static_cast(data.getDefault("ascender", 0).getDouble())), + xHeight(static_cast(data.getDefault("xHeight", 0).getDouble())) {} bool LineMeasurement::operator==(LineMeasurement const &rhs) const { return std::tie( diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index 56a72d3881fed..439fdb369af70 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -7,9 +7,13 @@ #include "TextLayoutManager.h" +#include + #include #include #include +#include +#include using namespace facebook::jni; @@ -28,77 +32,60 @@ TextMeasurement TextLayoutManager::measure( LayoutConstraints layoutConstraints) const { auto &attributedString = attributedStringBox.getValue(); - return measureCache_.get( + auto measurement = measureCache_.get( {attributedString, paragraphAttributes, layoutConstraints}, [&](TextMeasureCacheKey const &key) { - return doMeasure( - attributedString, paragraphAttributes, layoutConstraints); + auto telemetry = TransactionTelemetry::threadLocalTelemetry(); + if (telemetry) { + telemetry->willMeasureText(); + } + + auto measurement = mapBufferSerializationEnabled_ + ? doMeasureMapBuffer( + attributedString, paragraphAttributes, layoutConstraints) + : doMeasure( + attributedString, paragraphAttributes, layoutConstraints); + + if (telemetry) { + telemetry->didMeasureText(); + } + + return measurement; }); + + measurement.size = layoutConstraints.clamp(measurement.size); + return measurement; } TextMeasurement TextLayoutManager::measureCachedSpannableById( int64_t cacheId, ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const { - const jni::global_ref &fabricUIManager = - contextContainer_->at>("FabricUIManager"); - auto env = Environment::current(); auto attachmentPositions = env->NewFloatArray(0); - - static auto measure = - jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") - ->getMethod("measure"); - auto minimumSize = layoutConstraints.minimumSize; auto maximumSize = layoutConstraints.maximumSize; - local_ref componentName = make_jstring("RCTText"); folly::dynamic cacheIdMap = folly::dynamic::object; cacheIdMap["cacheId"] = cacheId; - local_ref attributedStringRNM = - ReadableNativeMap::newObjectCxxArgs(cacheIdMap); - local_ref paragraphAttributesRNM = - ReadableNativeMap::newObjectCxxArgs(toDynamic(paragraphAttributes)); - local_ref attributedStringRM = make_local( - reinterpret_cast(attributedStringRNM.get())); - local_ref paragraphAttributesRM = make_local( - reinterpret_cast(paragraphAttributesRNM.get())); - auto size = yogaMeassureToSize(measure( - fabricUIManager, + auto size = measureAndroidComponent( + contextContainer_, -1, // TODO: we should pass rootTag in - componentName.get(), - attributedStringRM.get(), - paragraphAttributesRM.get(), + "RCTText", + cacheIdMap, + toDynamic(paragraphAttributes), nullptr, minimumSize.width, maximumSize.width, minimumSize.height, maximumSize.height, - attachmentPositions)); + attachmentPositions); // Clean up allocated ref - it still takes up space in the JNI ref table even // though it's 0 length env->DeleteLocalRef(attachmentPositions); - // Explicitly release smart pointers to free up space faster in JNI tables - componentName.reset(); - attributedStringRM.reset(); - attributedStringRNM.reset(); - paragraphAttributesRM.reset(); - paragraphAttributesRNM.reset(); - // TODO: currently we do not support attachments for cached IDs - should we? auto attachments = TextMeasurement::Attachments{}; @@ -109,6 +96,10 @@ LinesMeasurements TextLayoutManager::measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { + if (mapBufferSerializationEnabled_) { + return measureLinesMapBuffer(attributedString, paragraphAttributes, size); + } + const jni::global_ref &fabricUIManager = contextContainer_->at>("FabricUIManager"); static auto measureLines = @@ -155,12 +146,52 @@ LinesMeasurements TextLayoutManager::measureLines( return lineMeasurements; } -TextMeasurement TextLayoutManager::doMeasure( +LinesMeasurements TextLayoutManager::measureLinesMapBuffer( AttributedString attributedString, ParagraphAttributes paragraphAttributes, - LayoutConstraints layoutConstraints) const { + Size size) const { const jni::global_ref &fabricUIManager = contextContainer_->at>("FabricUIManager"); + static auto measureLines = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measureLinesMapBuffer"); + + auto attributedStringMB = + ReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); + auto paragraphAttributesMB = + ReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); + + auto array = measureLines( + fabricUIManager, + attributedStringMB.get(), + paragraphAttributesMB.get(), + size.width, + size.height); + + auto dynamicArray = cthis(array)->consume(); + LinesMeasurements lineMeasurements; + lineMeasurements.reserve(dynamicArray.size()); + + for (auto const &data : dynamicArray) { + lineMeasurements.push_back(LineMeasurement(data)); + } + + // Explicitly release smart pointers to free up space faster in JNI tables + attributedStringMB.reset(); + paragraphAttributesMB.reset(); + + return lineMeasurements; +} + +TextMeasurement TextLayoutManager::doMeasure( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + LayoutConstraints layoutConstraints) const { + layoutConstraints.maximumSize.height = std::numeric_limits::infinity(); int attachmentsCount = 0; for (auto fragment : attributedString.getFragments()) { @@ -171,46 +202,22 @@ TextMeasurement TextLayoutManager::doMeasure( auto env = Environment::current(); auto attachmentPositions = env->NewFloatArray(attachmentsCount * 2); - static auto measure = - jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") - ->getMethod("measure"); - auto minimumSize = layoutConstraints.minimumSize; auto maximumSize = layoutConstraints.maximumSize; auto serializedAttributedString = toDynamic(attributedString); - local_ref componentName = make_jstring("RCTText"); - local_ref attributedStringRNM = - ReadableNativeMap::newObjectCxxArgs(serializedAttributedString); - local_ref paragraphAttributesRNM = - ReadableNativeMap::newObjectCxxArgs(toDynamic(paragraphAttributes)); - - local_ref attributedStringRM = make_local( - reinterpret_cast(attributedStringRNM.get())); - local_ref paragraphAttributesRM = make_local( - reinterpret_cast(paragraphAttributesRNM.get())); - auto size = yogaMeassureToSize(measure( - fabricUIManager, + auto size = measureAndroidComponent( + contextContainer_, -1, // TODO: we should pass rootTag in - componentName.get(), - attributedStringRM.get(), - paragraphAttributesRM.get(), + "RCTText", + serializedAttributedString, + toDynamic(paragraphAttributes), nullptr, minimumSize.width, maximumSize.width, minimumSize.height, maximumSize.height, - attachmentPositions)); + attachmentPositions); jfloat *attachmentData = env->GetFloatArrayElements(attachmentPositions, 0); @@ -226,8 +233,8 @@ TextMeasurement TextLayoutManager::doMeasure( float width = (float)fragment["width"].getDouble(); float height = (float)fragment["height"].getDouble(); - auto rect = facebook::react::Rect{{left, top}, - facebook::react::Size{width, height}}; + auto rect = facebook::react::Rect{ + {left, top}, facebook::react::Size{width, height}}; attachments.push_back(TextMeasurement::Attachment{rect, false}); attachmentIndex++; } @@ -239,12 +246,67 @@ TextMeasurement TextLayoutManager::doMeasure( attachmentPositions, attachmentData, JNI_ABORT); env->DeleteLocalRef(attachmentPositions); - // Explicitly release smart pointers to free up space faster in JNI tables - componentName.reset(); - attributedStringRM.reset(); - attributedStringRNM.reset(); - paragraphAttributesRM.reset(); - paragraphAttributesRNM.reset(); + return TextMeasurement{size, attachments}; +} + +TextMeasurement TextLayoutManager::doMeasureMapBuffer( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + LayoutConstraints layoutConstraints) const { + layoutConstraints.maximumSize.height = std::numeric_limits::infinity(); + + int attachmentsCount = 0; + for (auto fragment : attributedString.getFragments()) { + if (fragment.isAttachment()) { + attachmentsCount++; + } + } + auto env = Environment::current(); + auto attachmentPositions = env->NewFloatArray(attachmentsCount * 2); + + auto minimumSize = layoutConstraints.minimumSize; + auto maximumSize = layoutConstraints.maximumSize; + + auto attributedStringMap = toMapBuffer(attributedString); + auto paragraphAttributesMap = toMapBuffer(paragraphAttributes); + + auto size = measureAndroidComponentMapBuffer( + contextContainer_, + -1, // TODO: we should pass rootTag in + "RCTText", + attributedStringMap, + paragraphAttributesMap, + minimumSize.width, + maximumSize.width, + minimumSize.height, + maximumSize.height, + attachmentPositions); + + jfloat *attachmentData = env->GetFloatArrayElements(attachmentPositions, 0); + + auto attachments = TextMeasurement::Attachments{}; + if (attachmentsCount > 0) { + int attachmentIndex = 0; + for (auto fragment : attributedString.getFragments()) { + if (fragment.isAttachment()) { + float top = attachmentData[attachmentIndex * 2]; + float left = attachmentData[attachmentIndex * 2 + 1]; + float width = fragment.parentShadowView.layoutMetrics.frame.size.width; + float height = + fragment.parentShadowView.layoutMetrics.frame.size.height; + + auto rect = facebook::react::Rect{ + {left, top}, facebook::react::Size{width, height}}; + attachments.push_back(TextMeasurement::Attachment{rect, false}); + attachmentIndex++; + } + } + } + + // Clean up allocated ref + env->ReleaseFloatArrayElements( + attachmentPositions, attachmentData, JNI_ABORT); + env->DeleteLocalRef(attachmentPositions); return TextMeasurement{size, attachments}; } diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h b/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h index 681c54e22fb79..19ae11b2a7d62 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -26,7 +27,11 @@ using SharedTextLayoutManager = std::shared_ptr; class TextLayoutManager { public: TextLayoutManager(const ContextContainer::Shared &contextContainer) - : contextContainer_(contextContainer){}; + : contextContainer_(contextContainer) { + static auto value = + contextContainer->at("MapBufferSerializationEnabled"); + mapBufferSerializationEnabled_ = value; + } ~TextLayoutManager(); /* @@ -67,8 +72,19 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const; + TextMeasurement doMeasureMapBuffer( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + LayoutConstraints layoutConstraints) const; + + LinesMeasurements measureLinesMapBuffer( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + void *self_; ContextContainer::Shared contextContainer_; + bool mapBufferSerializationEnabled_; TextMeasureCache measureCache_{}; }; diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp b/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp index ffdfeae66d547..6d7f07ea6d1ed 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp @@ -13,15 +13,29 @@ namespace react { TextLayoutManager::~TextLayoutManager() {} void *TextLayoutManager::getNativeTextLayoutManager() const { - return self_; + return (void *)this; } TextMeasurement TextLayoutManager::measure( AttributedStringBox attributedStringBox, ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const { - return TextMeasurement{{0, 0}, {}}; + TextMeasurement::Attachments attachments; + for (auto const &fragment : attributedStringBox.getValue().getFragments()) { + if (fragment.isAttachment()) { + attachments.push_back( + TextMeasurement::Attachment{{{0, 0}, {0, 0}}, false}); + } + } + return TextMeasurement{{0, 0}, attachments}; } +LinesMeasurements TextLayoutManager::measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const { + return {}; +}; + } // namespace react } // namespace facebook diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h b/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h index 7033cdeafe3b4..87c6fdf4bfeca 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h @@ -28,8 +28,7 @@ using SharedTextLayoutManager = std::shared_ptr; */ class TextLayoutManager { public: - TextLayoutManager(const ContextContainer::Shared &contextContainer) - : contextContainer_(contextContainer){}; + TextLayoutManager(const ContextContainer::Shared &contextContainer) {} ~TextLayoutManager(); /* @@ -40,16 +39,20 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const; + /* + * Measures lines of `attributedString` using native text rendering + * infrastructure. + */ + LinesMeasurements measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager. */ void *getNativeTextLayoutManager() const; - - private: - void *self_; - - ContextContainer::Shared contextContainer_; }; } // namespace react diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.h b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.h index 373e78daac329..3a8ff540741bb 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.h +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.h @@ -35,6 +35,8 @@ NSAttributedString *RCTNSAttributedStringFromAttributedStringBox( facebook::react::AttributedStringBox RCTAttributedStringBoxFromNSAttributedString( NSAttributedString *nsAttributedString); +NSString *RCTNSStringFromStringApplyingTextTransform(NSString *string, facebook::react::TextTransform textTransform); + @interface RCTWeakEventEmitterWrapper : NSObject @property (nonatomic, assign) facebook::react::SharedEventEmitter eventEmitter; @end diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.mm b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.mm index 0f23297ad4afb..7e63b2df90b3c 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.mm +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTAttributedTextUtils.mm @@ -41,15 +41,16 @@ inline static UIFontWeight RCTUIFontWeightFromInteger(NSInteger fontWeight) assert(fontWeight > 50); assert(fontWeight < 950); - static UIFontWeight weights[] = {/* ~100 */ UIFontWeightUltraLight, - /* ~200 */ UIFontWeightThin, - /* ~300 */ UIFontWeightLight, - /* ~400 */ UIFontWeightRegular, - /* ~500 */ UIFontWeightMedium, - /* ~600 */ UIFontWeightSemibold, - /* ~700 */ UIFontWeightBold, - /* ~800 */ UIFontWeightHeavy, - /* ~900 */ UIFontWeightBlack}; + static UIFontWeight weights[] = { + /* ~100 */ UIFontWeightUltraLight, + /* ~200 */ UIFontWeightThin, + /* ~300 */ UIFontWeightLight, + /* ~400 */ UIFontWeightRegular, + /* ~500 */ UIFontWeightMedium, + /* ~600 */ UIFontWeightSemibold, + /* ~700 */ UIFontWeightBold, + /* ~800 */ UIFontWeightHeavy, + /* ~900 */ UIFontWeightBlack}; // The expression is designed to convert something like 760 or 830 to 7. return weights[(fontWeight + 50) / 100 - 1]; } @@ -287,6 +288,9 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex case AccessibilityRole::Tab: attributes[RCTTextAttributesAccessibilityRoleAttributeName] = @("tab"); break; + case AccessibilityRole::TabBar: + attributes[RCTTextAttributesAccessibilityRoleAttributeName] = @("tabbar"); + break; case AccessibilityRole::Tablist: attributes[RCTTextAttributesAccessibilityRoleAttributeName] = @("tablist"); break; @@ -319,8 +323,9 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex if (fragment.isAttachment()) { auto layoutMetrics = fragment.parentShadowView.layoutMetrics; - CGRect bounds = {.origin = {.x = layoutMetrics.frame.origin.x, .y = layoutMetrics.frame.origin.y}, - .size = {.width = layoutMetrics.frame.size.width, .height = layoutMetrics.frame.size.height}}; + CGRect bounds = { + .origin = {.x = layoutMetrics.frame.origin.x, .y = layoutMetrics.frame.origin.y}, + .size = {.width = layoutMetrics.frame.size.width, .height = layoutMetrics.frame.size.height}}; NSTextAttachment *attachment = [NSTextAttachment new]; attachment.image = placeholderImage; @@ -330,6 +335,11 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex } else { NSString *string = [NSString stringWithCString:fragment.string.c_str() encoding:NSUTF8StringEncoding]; + if (fragment.textAttributes.textTransform.hasValue()) { + auto textTransform = fragment.textAttributes.textTransform.value(); + string = RCTNSStringFromStringApplyingTextTransform(string, textTransform); + } + nsAttributedStringFragment = [[NSMutableAttributedString alloc] initWithString:string attributes:RCTNSTextAttributesFromTextAttributes(fragment.textAttributes)]; @@ -368,3 +378,17 @@ AttributedStringBox RCTAttributedStringBoxFromNSAttributedString(NSAttributedStr { return nsAttributedString.length ? AttributedStringBox{wrapManagedObject(nsAttributedString)} : AttributedStringBox{}; } + +NSString *RCTNSStringFromStringApplyingTextTransform(NSString *string, TextTransform textTransform) +{ + switch (textTransform) { + case TextTransform::Uppercase: + return [string uppercaseString]; + case TextTransform::Lowercase: + return [string lowercaseString]; + case TextTransform::Capitalize: + return [string capitalizedString]; + default: + return string; + } +} diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm index 13ca3c5ae2cd5..3cd00abda54da 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm @@ -75,12 +75,14 @@ - (TextMeasurement)measureNSAttributedString:(NSAttributedString *)attributedStr UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil]; - CGRect frame = {{glyphRect.origin.x, - glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender}, - attachmentSize}; + CGRect frame = { + {glyphRect.origin.x, + glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender}, + attachmentSize}; - auto rect = facebook::react::Rect{facebook::react::Point{frame.origin.x, frame.origin.y}, - facebook::react::Size{frame.size.width, frame.size.height}}; + auto rect = facebook::react::Rect{ + facebook::react::Point{frame.origin.x, frame.origin.y}, + facebook::react::Size{frame.size.width, frame.size.height}}; attachments.push_back(TextMeasurement::Attachment{rect, false}); }]; @@ -156,12 +158,13 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr auto rect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, facebook::react::Size{usedRect.size.width, usedRect.size.height}}; - auto line = LineMeasurement{std::string([renderedString UTF8String]), - rect, - -font.descender, - font.capHeight, - font.ascender, - font.xHeight}; + auto line = LineMeasurement{ + std::string([renderedString UTF8String]), + rect, + -font.descender, + font.capHeight, + font.ascender, + font.xHeight}; blockParagraphLines->push_back(line); }]; return paragraphLines; diff --git a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/TextLayoutManager.mm b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/TextLayoutManager.mm index 881bcf90b47a2..887f3726bac39 100644 --- a/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/TextLayoutManager.mm +++ b/android/ReactCommon/react/renderer/textlayoutmanager/platform/ios/TextLayoutManager.mm @@ -6,6 +6,7 @@ */ #include "TextLayoutManager.h" +#include #include #import "RCTTextLayoutManager.h" @@ -39,9 +40,20 @@ measurement = measureCache_.get( {attributedString, paragraphAttributes, layoutConstraints}, [&](TextMeasureCacheKey const &key) { - return [textLayoutManager measureAttributedString:attributedString - paragraphAttributes:paragraphAttributes - layoutConstraints:layoutConstraints]; + auto telemetry = TransactionTelemetry::threadLocalTelemetry(); + if (telemetry) { + telemetry->willMeasureText(); + } + + auto measurement = [textLayoutManager measureAttributedString:attributedString + paragraphAttributes:paragraphAttributes + layoutConstraints:layoutConstraints]; + + if (telemetry) { + telemetry->didMeasureText(); + } + + return measurement; }); break; } @@ -50,9 +62,19 @@ NSAttributedString *nsAttributedString = (NSAttributedString *)unwrapManagedObject(attributedStringBox.getOpaquePointer()); + auto telemetry = TransactionTelemetry::threadLocalTelemetry(); + if (telemetry) { + telemetry->willMeasureText(); + } + measurement = [textLayoutManager measureNSAttributedString:nsAttributedString paragraphAttributes:paragraphAttributes layoutConstraints:layoutConstraints]; + + if (telemetry) { + telemetry->didMeasureText(); + } + break; } } diff --git a/android/ReactCommon/react/renderer/components/picker/BUCK b/android/ReactCommon/react/renderer/timeline/BUCK similarity index 56% rename from android/ReactCommon/react/renderer/components/picker/BUCK rename to android/ReactCommon/react/renderer/timeline/BUCK index 9c0661e9370b2..a8888cb22f7c3 100644 --- a/android/ReactCommon/react/renderer/components/picker/BUCK +++ b/android/ReactCommon/react/renderer/timeline/BUCK @@ -4,11 +4,9 @@ load( "ANDROID", "APPLE", "CXX", - "YOGA_CXX_TARGET", "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", - "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -17,42 +15,33 @@ load( APPLE_COMPILER_FLAGS = get_apple_compiler_flags() rn_xplat_cxx_library( - name = "androidpicker", + name = "timeline", srcs = glob( - ["androidpicker/**/*.cpp"], + ["**/*.cpp"], exclude = glob(["tests/**/*.cpp"]), ), headers = glob( - ["androidpicker/**/*.h"], + ["**/*.h"], exclude = glob(["tests/**/*.h"]), ), header_namespace = "", exported_headers = subdir_glob( [ ("", "*.h"), - ("androidpicker/react/renderer/components/androidpicker", "*.h"), ], - prefix = "react/renderer/components/androidpicker", + prefix = "react/renderer/timeline", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], - cxx_tests = [":tests"], - fbandroid_deps = [ - react_native_target("jni/react/jni:jni"), - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_labels = ["supermodule:ios/isolation/infra.react_native"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, - labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", "-DWITH_FBSYSTRACE=1", ], + tests = [":tests"], visibility = ["PUBLIC"], deps = [ "//third-party/glog:glog", @@ -60,14 +49,12 @@ rn_xplat_cxx_library( "//xplat/folly:headers_only", "//xplat/folly:memory", "//xplat/folly:molly", - YOGA_CXX_TARGET, + react_native_xplat_target("react/utils:utils"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("react/renderer/core:core"), - react_native_xplat_target("react/renderer/graphics:graphics"), - react_native_xplat_target("react/renderer/components/view:view"), + react_native_xplat_target("react/renderer/components/root:root"), + react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/uimanager:uimanager"), - react_native_xplat_target("react/renderer/componentregistry:componentregistry"), - "//xplat/js/react-native-github:generated_components-rncore", ], ) @@ -78,20 +65,13 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], - platforms = ( - # `Apple` and `Android` flavors are disabled because the module depends on `textlayoutmanager` which requires real an Emulator/Simulator to run. - # At the same time, the code of tests does not rely on the simulator capabilities and it would be wasteful to add `fbandroid_use_instrumentation_test = True`. - # (Beware of this option though.) - # ANDROID, - # APPLE, - CXX - ), + platforms = (ANDROID, APPLE, CXX), deps = [ - ":androidpicker", + ":timeline", "//xplat/folly:molly", "//xplat/third-party/gmock:gtest", ], diff --git a/android/ReactCommon/react/renderer/timeline/Timeline.cpp b/android/ReactCommon/react/renderer/timeline/Timeline.cpp new file mode 100644 index 0000000000000..11dd457d8888a --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/Timeline.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "Timeline.h" + +#include + +namespace facebook { +namespace react { + +Timeline::Timeline(ShadowTree const &shadowTree) : shadowTree_(&shadowTree) { + record(shadowTree.getCurrentRevision().rootShadowNode); +}; + +#pragma mark - Public + +SurfaceId Timeline::getSurfaceId() const noexcept { + return shadowTree_->getSurfaceId(); +} + +void Timeline::pause() const noexcept { + std::lock_guard lock(mutex_); + assert(!paused_ && ""); + paused_ = true; +} + +void Timeline::resume() const noexcept { + std::lock_guard lock(mutex_); + + if (snapshots_.size() > 0) { + rewind(snapshots_.at(snapshots_.size() - 1)); + } + + assert(paused_ && ""); + paused_ = false; +} + +bool Timeline::isPaused() const noexcept { + std::lock_guard lock(mutex_); + return paused_; +} + +TimelineFrame::List Timeline::getFrames() const noexcept { + std::lock_guard lock(mutex_); + + auto frames = TimelineFrame::List{}; + frames.reserve(snapshots_.size()); + for (auto const &snapshot : snapshots_) { + frames.push_back(snapshot.getFrame()); + } + return frames; +} + +TimelineFrame Timeline::getCurrentFrame() const noexcept { + assert(snapshots_.size() > currentSnapshotIndex_); + return snapshots_.at(currentSnapshotIndex_).getFrame(); +} + +void Timeline::rewind(TimelineFrame const &frame) const noexcept { + std::lock_guard lock(mutex_); + rewind(snapshots_.at(frame.getIndex())); +} + +RootShadowNode::Unshared Timeline::shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const noexcept { + std::lock_guard lock(mutex_); + + if (rewinding_) { + return newRootShadowNode; + } + + record(newRootShadowNode); + + if (paused_) { + return nullptr; + } + + return newRootShadowNode; +} + +#pragma mark - Private & Internal + +void Timeline::record( + RootShadowNode::Shared const &rootShadowNode) const noexcept { + auto index = (int)snapshots_.size(); + snapshots_.push_back(TimelineSnapshot{rootShadowNode, index}); + + if (!paused_) { + currentSnapshotIndex_ = index; + } +} + +void Timeline::rewind(TimelineSnapshot const &snapshot) const noexcept { + std::lock_guard lock(mutex_); + + currentSnapshotIndex_ = snapshot.getFrame().getIndex(); + + assert(!rewinding_ && ""); + rewinding_ = true; + + auto rootShadowNode = snapshot.getRootShadowNode(); + + shadowTree_->commit( + [&](RootShadowNode const &oldRootShadowNode) -> RootShadowNode::Unshared { + return std::static_pointer_cast( + rootShadowNode->ShadowNode::clone({})); + }); + + assert(rewinding_ && ""); + rewinding_ = false; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/Timeline.h b/android/ReactCommon/react/renderer/timeline/Timeline.h new file mode 100644 index 0000000000000..8eaaf22677673 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/Timeline.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace facebook { +namespace react { + +class UIManager; + +class Timeline final { + friend class TimelineHandler; + friend class TimelineController; + + public: + Timeline(ShadowTree const &shadowTree); + + private: +#pragma mark - Private methods to be used by `TimelineHandler`. + + void pause() const noexcept; + void resume() const noexcept; + bool isPaused() const noexcept; + TimelineFrame::List getFrames() const noexcept; + TimelineFrame getCurrentFrame() const noexcept; + void rewind(TimelineFrame const &frame) const noexcept; + SurfaceId getSurfaceId() const noexcept; + +#pragma mark - Private methods to be used by `TimelineController`. + + RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const noexcept; + +#pragma mark - Private & Internal + + void record(RootShadowNode::Shared const &rootShadowNode) const noexcept; + void rewind(TimelineSnapshot const &snapshot) const noexcept; + + mutable std::recursive_mutex mutex_; + mutable ShadowTree const *shadowTree_{nullptr}; + mutable int currentSnapshotIndex_{0}; + mutable TimelineSnapshot::List snapshots_{}; + mutable bool paused_{false}; + mutable bool rewinding_{false}; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineController.cpp b/android/ReactCommon/react/renderer/timeline/TimelineController.cpp new file mode 100644 index 0000000000000..153650ae092f3 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineController.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "TimelineController.h" + +#include +#include + +namespace facebook { +namespace react { + +TimelineHandler TimelineController::enable(SurfaceId surfaceId) const { + assert(uiManager_); + + auto shadowTreePtr = (ShadowTree const *){}; + + uiManager_->getShadowTreeRegistry().visit( + surfaceId, + [&](ShadowTree const &shadowTree) { shadowTreePtr = &shadowTree; }); + + assert(shadowTreePtr); + + { + std::unique_lock lock(timelinesMutex_); + + auto timeline = std::make_unique(*shadowTreePtr); + auto handler = TimelineHandler{*timeline}; + timelines_.emplace(surfaceId, std::move(timeline)); + return handler; + } +} + +void TimelineController::disable(TimelineHandler &&handler) const { + std::unique_lock lock(timelinesMutex_); + + auto iterator = timelines_.find(handler.getSurfaceId()); + assert(iterator != timelines_.end()); + timelines_.erase(iterator); + handler.release(); +} + +void TimelineController::commitHookWasRegistered( + UIManager const &uiManager) const noexcept { + uiManager_ = &uiManager; +} + +void TimelineController::commitHookWasUnregistered( + UIManager const &uiManager) const noexcept { + uiManager_ = nullptr; +} + +RootShadowNode::Unshared TimelineController::shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const noexcept { + std::shared_lock lock(timelinesMutex_); + + assert(uiManager_ && "`uiManager_` must not be `nullptr`."); + + lastUpdatedSurface_ = shadowTree.getSurfaceId(); + + auto iterator = timelines_.find(shadowTree.getSurfaceId()); + if (iterator == timelines_.end()) { + return newRootShadowNode; + } + + auto &timeline = *iterator->second; + return timeline.shadowTreeWillCommit( + shadowTree, oldRootShadowNode, newRootShadowNode); +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineController.h b/android/ReactCommon/react/renderer/timeline/TimelineController.h new file mode 100644 index 0000000000000..0cfed408e7c79 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineController.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * Provides tools for introspecting the series of commits and associated + * side-effects, allowing to "rewind" UI to any particular commit from the past. + */ +class TimelineController final : public UIManagerCommitHook { + public: + using Shared = std::shared_ptr; + + /* + * Creates a `TimelineHandler` associated with given `SurfaceId` and starts + * the introspection process. + */ + TimelineHandler enable(SurfaceId surfaceId) const; + + /* + * Consumes and destroys a `TimelineHandler` instance triggering the + * destruction of all associated resources and stoping the introspection + * process. + */ + void disable(TimelineHandler &&handler) const; + + /* + * TO BE DELETED. + */ + SurfaceId lastUpdatedSurface() const { + return lastUpdatedSurface_; + } + +#pragma mark - UIManagerCommitHook + + RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) + const noexcept override; + + void commitHookWasRegistered( + UIManager const &uiManager) const noexcept override; + + void commitHookWasUnregistered( + UIManager const &uiManager) const noexcept override; + + private: + /* + * Protects all the data members. + */ + mutable better::shared_mutex timelinesMutex_; + + /* + * Owning collection of all running `Timeline` instances. + */ + mutable better::map> timelines_; + + mutable UIManager const *uiManager_; + mutable SurfaceId lastUpdatedSurface_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineFrame.cpp b/android/ReactCommon/react/renderer/timeline/TimelineFrame.cpp new file mode 100644 index 0000000000000..6b6f7883021ff --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineFrame.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "TimelineFrame.h" + +namespace facebook { +namespace react { + +TimelineFrame::TimelineFrame(int index, TelemetryTimePoint timePoint) noexcept + : index_(index), timePoint_(timePoint) {} + +int TimelineFrame::getIndex() const noexcept { + return index_; +} + +TelemetryTimePoint TimelineFrame::getTimePoint() const noexcept { + return timePoint_; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineFrame.h b/android/ReactCommon/react/renderer/timeline/TimelineFrame.h new file mode 100644 index 0000000000000..a6374cb509a61 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineFrame.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * Represents a reference to a commit from the past. + * The reference can be safely used to address a particular commit from non-core + * code. + */ +class TimelineFrame final { + friend class TimelineSnapshot; + + /* + * Constructor is private and must be called by `TimelineSnapshot` only. + */ + TimelineFrame(int index, TelemetryTimePoint timePoint) noexcept; + + public: + using List = std::vector; + + TimelineFrame() = delete; + TimelineFrame(TimelineFrame const &timelineFrame) noexcept = default; + TimelineFrame &operator=(TimelineFrame const &other) noexcept = default; + + int getIndex() const noexcept; + TelemetryTimePoint getTimePoint() const noexcept; + + private: + int index_; + TelemetryTimePoint timePoint_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineHandler.cpp b/android/ReactCommon/react/renderer/timeline/TimelineHandler.cpp new file mode 100644 index 0000000000000..953e305d80b84 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineHandler.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "TimelineHandler.h" + +#include + +#include + +namespace facebook { +namespace react { + +TimelineHandler::TimelineHandler(Timeline const &timeline) noexcept + : timeline_(&timeline) {} + +TimelineHandler::TimelineHandler(TimelineHandler &&other) noexcept { + this->operator=(std::move(other)); +} + +TimelineHandler::~TimelineHandler() noexcept { + if (timeline_ != nullptr) { + // Improper deallocation indicates a severe error in application logic: + abort(); + } +} + +TimelineHandler &TimelineHandler::operator=(TimelineHandler &&other) noexcept { + assert(other.timeline_ && "Moving from an empty `TimelineHandler`."); + timeline_ = other.timeline_; + other.timeline_ = nullptr; + return *this; +} + +#pragma mark - Public + +void TimelineHandler::pause() const noexcept { + ensureNotEmpty(); + timeline_->pause(); +} + +void TimelineHandler::resume() const noexcept { + ensureNotEmpty(); + timeline_->resume(); +} + +bool TimelineHandler::isPaused() const noexcept { + ensureNotEmpty(); + return timeline_->isPaused(); +} + +TimelineFrame TimelineHandler::getCurrentFrame() const noexcept { + ensureNotEmpty(); + return timeline_->getCurrentFrame(); +} + +TimelineFrame::List TimelineHandler::getFrames() const noexcept { + ensureNotEmpty(); + return timeline_->getFrames(); +} + +void TimelineHandler::rewind(TimelineFrame const &frame) const noexcept { + ensureNotEmpty(); + return timeline_->rewind(frame); +} + +void TimelineHandler::seek(int delta) const noexcept { + ensureNotEmpty(); + auto frames = timeline_->getFrames(); + auto currentFrame = timeline_->getCurrentFrame(); + auto seekFrameIndex = currentFrame.getIndex() + delta; + seekFrameIndex = + std::min((int)frames.size() - 1, std::max(0, seekFrameIndex)); + timeline_->rewind(frames.at(seekFrameIndex)); +} + +#pragma mark - Private + +SurfaceId TimelineHandler::getSurfaceId() const noexcept { + return timeline_->getSurfaceId(); +} + +void TimelineHandler::release() noexcept { + timeline_ = nullptr; +} + +void TimelineHandler::ensureNotEmpty() const noexcept { + if (!timeline_) { + abort(); + } +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineHandler.h b/android/ReactCommon/react/renderer/timeline/TimelineHandler.h new file mode 100644 index 0000000000000..3065eefa7fec0 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineHandler.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +class Timeline; + +class TimelineHandler final { + public: + ~TimelineHandler() noexcept; + + /* + * Movable, not copyable. + */ + TimelineHandler(TimelineHandler &&timelineHandler) noexcept; + TimelineHandler(TimelineHandler const &timelineHandler) = delete; + TimelineHandler &operator=(TimelineHandler &&other) noexcept; + TimelineHandler &operator=(TimelineHandler const &other) = delete; + + /* + * Stops (or resumes) mounting of new commits. + * A surface has to be paused to allow rewinding the UI to some past commit. + */ + void pause() const noexcept; + void resume() const noexcept; + bool isPaused() const noexcept; + + /* + * Provides access to recorded frames. + */ + TimelineFrame::List getFrames() const noexcept; + TimelineFrame getCurrentFrame() const noexcept; + + /* + * Rewinds the UI to a given frame. + */ + void rewind(TimelineFrame const &frame) const noexcept; + + /* + * Rewinds the UI for a given number of frames back or forward. + */ + void seek(int delta) const noexcept; + + private: + friend class TimelineController; + + /* + * Can only be constructed by `TimelineController`. + */ + TimelineHandler(Timeline const &timeline) noexcept; + + /* + * Must be called before deallocation to make it not crash. + * Must be only called by `TimelineController`. + */ + void release() noexcept; + + /* + * Returns a `SurfaceId` of the assigned Surface. + */ + SurfaceId getSurfaceId() const noexcept; + + void ensureNotEmpty() const noexcept; + + Timeline const *timeline_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineSnapshot.cpp b/android/ReactCommon/react/renderer/timeline/TimelineSnapshot.cpp new file mode 100644 index 0000000000000..fd460b1ef22f2 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineSnapshot.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "TimelineSnapshot.h" + +#include + +namespace facebook { +namespace react { + +TimelineSnapshot::TimelineSnapshot( + RootShadowNode::Shared const &rootShadowNode, + int index) noexcept + : rootShadowNode_(rootShadowNode), + frame_(TimelineFrame{index, telemetryTimePointNow()}) {} + +RootShadowNode::Shared TimelineSnapshot::getRootShadowNode() const noexcept { + return rootShadowNode_; +} + +TimelineFrame TimelineSnapshot::getFrame() const noexcept { + return frame_; +} + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/timeline/TimelineSnapshot.h b/android/ReactCommon/react/renderer/timeline/TimelineSnapshot.h new file mode 100644 index 0000000000000..59d022b3f18d6 --- /dev/null +++ b/android/ReactCommon/react/renderer/timeline/TimelineSnapshot.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook { +namespace react { + +/* + * Represents a reference to a commit from the past used by `Timeline`. + */ +class TimelineSnapshot final { + public: + using List = std::vector; + + TimelineSnapshot( + RootShadowNode::Shared const &rootShadowNode, + int index) noexcept; + + TimelineFrame getFrame() const noexcept; + RootShadowNode::Shared getRootShadowNode() const noexcept; + + private: + RootShadowNode::Shared rootShadowNode_; + TimelineFrame frame_; +}; + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/renderer/uimanager/Android.mk b/android/ReactCommon/react/renderer/uimanager/Android.mk index 45f45b827b291..47975cd7dad25 100644 --- a/android/ReactCommon/react/renderer/uimanager/Android.mk +++ b/android/ReactCommon/react/renderer/uimanager/Android.mk @@ -17,11 +17,11 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := libreact_render_graphics libfolly_futures libruntimeexecutor libreact_render_componentregistry glog libreactconfig libfolly_json libjsi libreact_render_core libreact_render_debug libreact_render_components_view libreact_render_components_root libreact_render_mounting +LOCAL_SHARED_LIBRARIES := libreact_render_graphics libfolly_futures libruntimeexecutor libreact_render_componentregistry glog libreactconfig libfolly_json libjsi libreact_render_core libreact_render_debug librrc_view librrc_root libreact_render_mounting libreact_debug libreact_render_leakchecker include $(BUILD_SHARED_LIBRARY) @@ -33,7 +33,9 @@ $(call import-module,react/renderer/components/root) $(call import-module,react/renderer/components/view) $(call import-module,react/renderer/componentregistry) $(call import-module,react/renderer/core) +$(call import-module,react/renderer/leakchecker) $(call import-module,react/renderer/debug) $(call import-module,react/renderer/graphics) $(call import-module,react/renderer/mounting) +$(call import-module,react/debug) $(call import-module,runtimeexecutor) diff --git a/android/ReactCommon/react/renderer/uimanager/BUCK b/android/ReactCommon/react/renderer/uimanager/BUCK index 494f606a0531b..d518a76c385a0 100644 --- a/android/ReactCommon/react/renderer/uimanager/BUCK +++ b/android/ReactCommon/react/renderer/uimanager/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -7,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -31,12 +31,6 @@ rn_xplat_cxx_library( ], prefix = "react/renderer/uimanager", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, @@ -58,9 +52,11 @@ rn_xplat_cxx_library( "//xplat/jsi:JSIDynamic", "//xplat/jsi:jsi", react_native_xplat_target("react/config:config"), + react_native_xplat_target("react/debug:debug"), react_native_xplat_target("react/renderer/components/view:view"), react_native_xplat_target("react/renderer/mounting:mounting"), react_native_xplat_target("react/renderer/core:core"), + react_native_xplat_target("react/renderer/leakchecker:leakchecker"), react_native_xplat_target("react/renderer/componentregistry:componentregistry"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("runtimeexecutor:runtimeexecutor"), @@ -74,7 +70,7 @@ fb_xplat_cxx_test( compiler_flags = [ "-fexceptions", "-frtti", - "-std=c++14", + "-std=c++17", "-Wall", ], contacts = ["oncall+react_native@xmail.facebook.com"], diff --git a/android/ReactCommon/react/renderer/uimanager/LayoutAnimationStatusDelegate.h b/android/ReactCommon/react/renderer/uimanager/LayoutAnimationStatusDelegate.h index b87b96f68f73c..cad196eb98230 100644 --- a/android/ReactCommon/react/renderer/uimanager/LayoutAnimationStatusDelegate.h +++ b/android/ReactCommon/react/renderer/uimanager/LayoutAnimationStatusDelegate.h @@ -7,8 +7,7 @@ #pragma once -namespace facebook { -namespace react { +namespace facebook::react { class LayoutAnimationStatusDelegate { public: @@ -26,5 +25,4 @@ class LayoutAnimationStatusDelegate { virtual void onAllAnimationsComplete() = 0; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManager.cpp b/android/ReactCommon/react/renderer/uimanager/UIManager.cpp index 8a55d7e58822f..56bb2bad80afa 100644 --- a/android/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/android/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -7,14 +7,35 @@ #include "UIManager.h" +#include +#include #include #include #include +#include +#include #include -namespace facebook { -namespace react { +namespace facebook::react { + +static std::unique_ptr constructLeakCheckerIfNeeded( + RuntimeExecutor const &runtimeExecutor) { +#ifdef REACT_NATIVE_DEBUG + return std::make_unique(runtimeExecutor); +#else + return {}; +#endif +} + +UIManager::UIManager( + RuntimeExecutor const &runtimeExecutor, + BackgroundExecutor const &backgroundExecutor, + ContextContainer::Shared contextContainer) + : runtimeExecutor_(runtimeExecutor), + backgroundExecutor_(backgroundExecutor), + contextContainer_(contextContainer), + leakChecker_(constructLeakCheckerIfNeeded(runtimeExecutor)) {} UIManager::~UIManager() { LOG(WARNING) << "UIManager::~UIManager() was called (address: " << this @@ -33,10 +54,13 @@ SharedShadowNode UIManager::createNode( auto fallbackDescriptor = componentDescriptorRegistry_->getFallbackComponentDescriptor(); - auto family = componentDescriptor.createFamily( - ShadowNodeFamilyFragment{tag, surfaceId, nullptr}, - std::move(eventTarget)); - auto const props = componentDescriptor.cloneProps(nullptr, rawProps); + PropsParserContext propsParserContext{surfaceId, *contextContainer_.get()}; + + auto const fragment = ShadowNodeFamilyFragment{tag, surfaceId, nullptr}; + auto family = + componentDescriptor.createFamily(fragment, std::move(eventTarget)); + auto const props = + componentDescriptor.cloneProps(propsParserContext, nullptr, rawProps); auto const state = componentDescriptor.createInitialState(ShadowNodeFragment{props}, family); @@ -47,7 +71,9 @@ SharedShadowNode UIManager::createNode( fallbackDescriptor->getComponentHandle() == componentDescriptor.getComponentHandle() ? componentDescriptor.cloneProps( - props, RawProps(folly::dynamic::object("name", name))) + propsParserContext, + props, + RawProps(folly::dynamic::object("name", name))) : props, /* .children = */ ShadowNodeFragment::childrenPlaceholder(), /* .state = */ state, @@ -55,7 +81,10 @@ SharedShadowNode UIManager::createNode( family); if (delegate_) { - delegate_->uiManagerDidCreateShadowNode(shadowNode); + delegate_->uiManagerDidCreateShadowNode(*shadowNode.get()); + } + if (leakChecker_) { + leakChecker_->uiManagerDidCreateShadowNodeFamily(family); } return shadowNode; @@ -67,17 +96,25 @@ SharedShadowNode UIManager::cloneNode( const RawProps *rawProps) const { SystraceSection s("UIManager::cloneNode"); + PropsParserContext propsParserContext{ + shadowNode->getFamily().getSurfaceId(), *contextContainer_.get()}; + auto &componentDescriptor = shadowNode->getComponentDescriptor(); auto clonedShadowNode = componentDescriptor.cloneShadowNode( *shadowNode, { /* .props = */ rawProps ? componentDescriptor.cloneProps( - shadowNode->getProps(), *rawProps) + propsParserContext, shadowNode->getProps(), *rawProps) : ShadowNodeFragment::propsPlaceholder(), /* .children = */ children, }); + if (delegate_) { + delegate_->uiManagerDidCloneShadowNode( + *shadowNode.get(), *clonedShadowNode.get()); + } + return clonedShadowNode; } @@ -110,33 +147,88 @@ void UIManager::completeSurface( }); } -void UIManager::setJSResponder( - const ShadowNode::Shared &shadowNode, - const bool blockNativeResponder) const { +void UIManager::setIsJSResponder( + ShadowNode::Shared const &shadowNode, + bool isJSResponder, + bool blockNativeResponder) const { if (delegate_) { - delegate_->uiManagerDidSetJSResponder( - shadowNode->getSurfaceId(), shadowNode, blockNativeResponder); + delegate_->uiManagerDidSetIsJSResponder( + shadowNode, isJSResponder, blockNativeResponder); } } -void UIManager::clearJSResponder() const { - if (delegate_) { - delegate_->uiManagerDidClearJSResponder(); +void UIManager::startSurface( + ShadowTree::Unique &&shadowTree, + std::string const &moduleName, + folly::dynamic const &props, + DisplayMode displayMode) const { + SystraceSection s("UIManager::startSurface"); + + auto surfaceId = shadowTree->getSurfaceId(); + shadowTreeRegistry_.add(std::move(shadowTree)); + + runtimeExecutor_([=](jsi::Runtime &runtime) { + SystraceSection s("UIManager::startSurface::onRuntime"); + auto uiManagerBinding = UIManagerBinding::getBinding(runtime); + if (!uiManagerBinding) { + return; + } + + uiManagerBinding->startSurface( + runtime, surfaceId, moduleName, props, displayMode); + }); +} + +void UIManager::setSurfaceProps( + SurfaceId surfaceId, + std::string const &moduleName, + folly::dynamic const &props, + DisplayMode displayMode) const { + SystraceSection s("UIManager::setSurfaceProps"); + + runtimeExecutor_([=](jsi::Runtime &runtime) { + auto uiManagerBinding = UIManagerBinding::getBinding(runtime); + if (!uiManagerBinding) { + return; + } + + uiManagerBinding->setSurfaceProps( + runtime, surfaceId, moduleName, props, displayMode); + }); +} + +ShadowTree::Unique UIManager::stopSurface(SurfaceId surfaceId) const { + SystraceSection s("UIManager::stopSurface"); + + // Stop any ongoing animations. + stopSurfaceForAnimationDelegate(surfaceId); + + // Waiting for all concurrent commits to be finished and unregistering the + // `ShadowTree`. + auto shadowTree = getShadowTreeRegistry().remove(surfaceId); + + // We execute JavaScript/React part of the process at the very end to minimize + // any visible side-effects of stopping the Surface. Any possible commits from + // the JavaScript side will not be able to reference a `ShadowTree` and will + // fail silently. + runtimeExecutor_([=](jsi::Runtime &runtime) { + auto uiManagerBinding = UIManagerBinding::getBinding(runtime); + if (!uiManagerBinding) { + return; + } + + uiManagerBinding->stopSurface(runtime, surfaceId); + }); + + if (leakChecker_) { + leakChecker_->stopSurface(surfaceId); } + + return shadowTree; } ShadowNode::Shared UIManager::getNewestCloneOfShadowNode( ShadowNode const &shadowNode) const { - auto findNewestChildInParent = - [&](auto const &parentNode) -> ShadowNode::Shared { - for (auto const &child : parentNode.getChildren()) { - if (ShadowNode::sameFamily(*child, shadowNode)) { - return child; - } - } - return nullptr; - }; - auto ancestorShadowNode = ShadowNode::Shared{}; shadowTreeRegistry_.visit( shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) { @@ -153,7 +245,8 @@ ShadowNode::Shared UIManager::getNewestCloneOfShadowNode( return nullptr; } - return findNewestChildInParent(ancestors.rbegin()->first.get()); + auto pair = ancestors.rbegin(); + return pair->first.get().getChildren().at(pair->second); } ShadowNode::Shared UIManager::findNodeAtPoint( @@ -199,8 +292,7 @@ LayoutMetrics UIManager::getRelativeLayoutMetrics( shadowNode.getFamily(), *layoutableAncestorShadowNode, policy); } -void UIManager::updateStateWithAutorepeat( - StateUpdate const &stateUpdate) const { +void UIManager::updateState(StateUpdate const &stateUpdate) const { auto &callback = stateUpdate.callback; auto &family = stateUpdate.family; auto &componentDescriptor = family->getComponentDescriptor(); @@ -238,43 +330,6 @@ void UIManager::updateStateWithAutorepeat( }); } -void UIManager::updateState(StateUpdate const &stateUpdate) const { - if (stateUpdate.autorepeat || experimentEnableStateUpdateWithAutorepeat) { - updateStateWithAutorepeat(stateUpdate); - return; - } - - auto &callback = stateUpdate.callback; - auto &family = stateUpdate.family; - auto &componentDescriptor = family->getComponentDescriptor(); - - shadowTreeRegistry_.visit( - family->getSurfaceId(), [&](ShadowTree const &shadowTree) { - auto status = shadowTree.tryCommit([&](RootShadowNode const - &oldRootShadowNode) { - return std::static_pointer_cast( - oldRootShadowNode.cloneTree( - *family, [&](ShadowNode const &oldShadowNode) { - auto newData = - callback(oldShadowNode.getState()->getDataPointer()); - auto newState = - componentDescriptor.createState(*family, newData); - - return oldShadowNode.clone({ - /* .props = */ ShadowNodeFragment::propsPlaceholder(), - /* .children = */ - ShadowNodeFragment::childrenPlaceholder(), - /* .state = */ newState, - }); - })); - }); - if (status != ShadowTree::CommitStatus::Succeeded && - stateUpdate.failureCallback) { - stateUpdate.failureCallback(); - } - }); -} - void UIManager::dispatchCommand( const ShadowNode::Shared &shadowNode, std::string const &commandName, @@ -284,6 +339,14 @@ void UIManager::dispatchCommand( } } +void UIManager::sendAccessibilityEvent( + const ShadowNode::Shared &shadowNode, + std::string const &eventType) { + if (delegate_) { + delegate_->uiManagerDidSendAccessibilityEvent(shadowNode, eventType); + } +} + void UIManager::configureNextLayoutAnimation( jsi::Runtime &runtime, RawValue const &config, @@ -311,27 +374,56 @@ UIManagerDelegate *UIManager::getDelegate() { return delegate_; } -void UIManager::setBackgroundExecutor( - BackgroundExecutor const &backgroundExecutor) { - backgroundExecutor_ = backgroundExecutor; -} - void UIManager::visitBinding( - std::function callback) - const { - if (!uiManagerBinding_) { - return; + std::function callback, + jsi::Runtime &runtime) const { + auto uiManagerBinding = UIManagerBinding::getBinding(runtime); + if (uiManagerBinding) { + callback(*uiManagerBinding); } - - callback(*uiManagerBinding_); } ShadowTreeRegistry const &UIManager::getShadowTreeRegistry() const { return shadowTreeRegistry_; } +void UIManager::registerCommitHook( + UIManagerCommitHook const &commitHook) const { + std::unique_lock lock(commitHookMutex_); + react_native_assert( + std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook) == + commitHooks_.end()); + commitHook.commitHookWasRegistered(*this); + commitHooks_.push_back(&commitHook); +} + +void UIManager::unregisterCommitHook( + UIManagerCommitHook const &commitHook) const { + std::unique_lock lock(commitHookMutex_); + auto iterator = + std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook); + react_native_assert(iterator != commitHooks_.end()); + commitHooks_.erase(iterator); + commitHook.commitHookWasUnregistered(*this); +} + #pragma mark - ShadowTreeDelegate +RootShadowNode::Unshared UIManager::shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const { + std::shared_lock lock(commitHookMutex_); + + auto resultRootShadowNode = newRootShadowNode; + for (auto const *commitHook : commitHooks_) { + resultRootShadowNode = commitHook->shadowTreeWillCommit( + shadowTree, oldRootShadowNode, resultRootShadowNode); + } + + return resultRootShadowNode; +} + void UIManager::shadowTreeDidFinishTransaction( ShadowTree const &shadowTree, MountingCoordinator::Shared const &mountingCoordinator) const { @@ -348,7 +440,7 @@ void UIManager::setAnimationDelegate(UIManagerAnimationDelegate *delegate) { animationDelegate_ = delegate; } -void UIManager::stopSurfaceForAnimationDelegate(SurfaceId surfaceId) { +void UIManager::stopSurfaceForAnimationDelegate(SurfaceId surfaceId) const { if (animationDelegate_ != nullptr) { animationDelegate_->stopSurface(surfaceId); } @@ -364,5 +456,4 @@ void UIManager::animationTick() { } } -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManager.h b/android/ReactCommon/react/renderer/uimanager/UIManager.h index 6eb517fd9dc82..e24434cc9bcc7 100644 --- a/android/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/android/ReactCommon/react/renderer/uimanager/UIManager.h @@ -10,24 +10,33 @@ #include #include +#include + #include #include #include #include +#include #include #include #include #include #include #include +#include -namespace facebook { -namespace react { +namespace facebook::react { class UIManagerBinding; +class UIManagerCommitHook; class UIManager final : public ShadowTreeDelegate { public: + UIManager( + RuntimeExecutor const &runtimeExecutor, + BackgroundExecutor const &backgroundExecutor, + ContextContainer::Shared contextContainer); + ~UIManager(); void setComponentDescriptorRegistry( @@ -41,8 +50,6 @@ class UIManager final : public ShadowTreeDelegate { void setDelegate(UIManagerDelegate *delegate); UIManagerDelegate *getDelegate(); - void setBackgroundExecutor(BackgroundExecutor const &backgroundExecutor); - /** * Sets and gets the UIManager's Animation APIs delegate. * The delegate is stored as a raw pointer, so the owner must null @@ -53,7 +60,7 @@ class UIManager final : public ShadowTreeDelegate { /** * Execute stopSurface on any UIMAnagerAnimationDelegate. */ - void stopSurfaceForAnimationDelegate(SurfaceId surfaceId); + void stopSurfaceForAnimationDelegate(SurfaceId surfaceId) const; void animationTick(); @@ -64,8 +71,33 @@ class UIManager final : public ShadowTreeDelegate { * The callback is called synchronously on the same thread. */ void visitBinding( - std::function callback) - const; + std::function callback, + jsi::Runtime &runtime) const; + + /* + * Registers and unregisters a commit hook. + */ + void registerCommitHook(UIManagerCommitHook const &commitHook) const; + void unregisterCommitHook(UIManagerCommitHook const &commitHook) const; + + ShadowNode::Shared getNewestCloneOfShadowNode( + ShadowNode const &shadowNode) const; + +#pragma mark - Surface Start & Stop + + void startSurface( + ShadowTree::Unique &&shadowTree, + std::string const &moduleName, + folly::dynamic const &props, + DisplayMode displayMode) const; + + void setSurfaceProps( + SurfaceId surfaceId, + std::string const &moduleName, + folly::dynamic const &props, + DisplayMode displayMode) const; + + ShadowTree::Unique stopSurface(SurfaceId surfaceId) const; #pragma mark - ShadowTreeDelegate @@ -73,14 +105,18 @@ class UIManager final : public ShadowTreeDelegate { ShadowTree const &shadowTree, MountingCoordinator::Shared const &mountingCoordinator) const override; - /* - * Temporary flags. - */ - bool experimentEnableStateUpdateWithAutorepeat{false}; + RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const override; private: friend class UIManagerBinding; friend class Scheduler; + friend class SurfaceHandler; + + // `TimelineController` needs to call private `getShadowTreeRegistry()`. + friend class TimelineController; ShadowNode::Shared createNode( Tag tag, @@ -103,19 +139,15 @@ class UIManager final : public ShadowTreeDelegate { SharedShadowNodeUnsharedList const &rootChildren, ShadowTree::CommitOptions commitOptions) const; - void setJSResponder( - const ShadowNode::Shared &shadowNode, - const bool blockNativeResponder) const; - - void clearJSResponder() const; + void setIsJSResponder( + ShadowNode::Shared const &shadowNode, + bool isJSResponder, + bool blockNativeResponder) const; ShadowNode::Shared findNodeAtPoint( ShadowNode::Shared const &shadowNode, Point point) const; - ShadowNode::Shared getNewestCloneOfShadowNode( - ShadowNode const &shadowNode) const; - /* * Returns layout metrics of given `shadowNode` relative to * `ancestorShadowNode` (relative to the root node in case if provided @@ -131,13 +163,16 @@ class UIManager final : public ShadowTreeDelegate { * and performs a commit. */ void updateState(StateUpdate const &stateUpdate) const; - void updateStateWithAutorepeat(StateUpdate const &stateUpdate) const; void dispatchCommand( const ShadowNode::Shared &shadowNode, std::string const &commandName, folly::dynamic const args) const; + void sendAccessibilityEvent( + const ShadowNode::Shared &shadowNode, + std::string const &eventType); + /** * Configure a LayoutAnimation to happen on the next commit. * This API configures a global LayoutAnimation starting from the root node. @@ -153,16 +188,15 @@ class UIManager final : public ShadowTreeDelegate { SharedComponentDescriptorRegistry componentDescriptorRegistry_; UIManagerDelegate *delegate_; UIManagerAnimationDelegate *animationDelegate_{nullptr}; - UIManagerBinding *uiManagerBinding_; + RuntimeExecutor const runtimeExecutor_{}; ShadowTreeRegistry shadowTreeRegistry_{}; - BackgroundExecutor backgroundExecutor_{}; + BackgroundExecutor const backgroundExecutor_{}; + ContextContainer::Shared contextContainer_; + + mutable better::shared_mutex commitHookMutex_; + mutable std::vector commitHooks_; - // Used only when BackgroundExecutor is enabled. - // Property is used to keep count of `completeRoot` events to - // determine whether a commit should be cancelled. Only to be used - // inside UIManagerBinding. - std::atomic_uint_fast8_t completeRootEventCounter_{0}; + std::unique_ptr leakChecker_; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h b/android/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h index 9e55d1f3862e9..dc4417df99a13 100644 --- a/android/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h +++ b/android/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h @@ -12,12 +12,11 @@ #include #include -namespace facebook { -namespace react { +namespace facebook::react { class UIManagerAnimationDelegate { public: - virtual ~UIManagerAnimationDelegate(){}; + virtual ~UIManagerAnimationDelegate() = default; /* * Configure a LayoutAnimation. @@ -48,5 +47,4 @@ class UIManagerAnimationDelegate { virtual void stopSurface(SurfaceId surfaceId) = 0; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 9dac8a1cce918..cbc68035ad826 100644 --- a/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -7,15 +7,16 @@ #include "UIManagerBinding.h" -#include - #include #include +#include +#include +#include +#include -namespace facebook { -namespace react { +namespace facebook::react { -static jsi::Object getModule( +static jsi::Value getModule( jsi::Runtime &runtime, std::string const &moduleName) { auto batchedBridge = @@ -29,12 +30,36 @@ static jsi::Object getModule( if (!moduleAsValue.isObject()) { LOG(ERROR) << "getModule of " << moduleName << " is not an object"; } - assert(moduleAsValue.isObject()); - return moduleAsValue.asObject(runtime); + react_native_assert(moduleAsValue.isObject()); + return moduleAsValue; +} + +static bool checkBatchedBridgeIsActive(jsi::Runtime &runtime) { + if (!runtime.global().hasProperty(runtime, "__fbBatchedBridge")) { + LOG(ERROR) + << "getPropertyAsObject: property '__fbBatchedBridge' is undefined, expected an Object"; + return false; + } + return true; +} + +static bool checkGetCallableModuleIsActive(jsi::Runtime &runtime) { + if (!checkBatchedBridgeIsActive(runtime)) { + return false; + } + auto batchedBridge = + runtime.global().getPropertyAsObject(runtime, "__fbBatchedBridge"); + if (!batchedBridge.hasProperty(runtime, "getCallableModule")) { + LOG(ERROR) + << "getPropertyAsFunction: function 'getCallableModule' is undefined, expected a Function"; + return false; + } + return true; } std::shared_ptr UIManagerBinding::createAndInstallIfNeeded( - jsi::Runtime &runtime) { + jsi::Runtime &runtime, + RuntimeExecutor const &runtimeExecutor) { auto uiManagerModuleName = "nativeFabricUIManager"; auto uiManagerValue = @@ -42,7 +67,7 @@ std::shared_ptr UIManagerBinding::createAndInstallIfNeeded( if (uiManagerValue.isUndefined()) { // The global namespace does not have an instance of the binding; // we need to create, install and return it. - auto uiManagerBinding = std::make_shared(); + auto uiManagerBinding = std::make_shared(runtimeExecutor); auto object = jsi::Object::createFromHostObject(runtime, uiManagerBinding); runtime.global().setProperty( runtime, uiManagerModuleName, std::move(object)); @@ -55,35 +80,89 @@ std::shared_ptr UIManagerBinding::createAndInstallIfNeeded( return uiManagerObject.getHostObject(runtime); } +std::shared_ptr UIManagerBinding::getBinding( + jsi::Runtime &runtime) { + auto uiManagerModuleName = "nativeFabricUIManager"; + + auto uiManagerValue = + runtime.global().getProperty(runtime, uiManagerModuleName); + if (uiManagerValue.isUndefined()) { + return nullptr; + } + + auto uiManagerObject = uiManagerValue.asObject(runtime); + return uiManagerObject.getHostObject(runtime); +} + +UIManagerBinding::UIManagerBinding(RuntimeExecutor const &runtimeExecutor) + : runtimeExecutor_(runtimeExecutor) {} + UIManagerBinding::~UIManagerBinding() { LOG(WARNING) << "UIManagerBinding::~UIManagerBinding() was called (address: " << this << ")."; - - // We must detach the `UIBinding` on deallocation to prevent accessing - // deallocated `UIManagerBinding`. - // Since `UIManagerBinding` retains `UIManager`, `UIManager` always overlive - // `UIManagerBinding`, therefore we don't need similar logic in `UIManager`'s - // destructor. - attach(nullptr); } void UIManagerBinding::attach(std::shared_ptr const &uiManager) { - if (uiManager_) { - uiManager_->uiManagerBinding_ = nullptr; + uiManager_ = uiManager; +} + +static jsi::Value callMethodOfModule( + jsi::Runtime &runtime, + std::string const &moduleName, + std::string const &methodName, + std::initializer_list args) { + if (checkGetCallableModuleIsActive(runtime)) { + auto module = getModule(runtime, moduleName.c_str()); + if (module.isObject()) { + jsi::Object object = module.asObject(runtime); + react_native_assert(object.hasProperty(runtime, methodName.c_str())); + if (object.hasProperty(runtime, methodName.c_str())) { + auto method = object.getPropertyAsFunction(runtime, methodName.c_str()); + return method.callWithThis(runtime, object, args); + } else { + LOG(ERROR) << "getPropertyAsFunction: property '" << methodName + << "' is undefined, expected a Function"; + } + } } - uiManager_ = uiManager; + return jsi::Value::undefined(); +} + +jsi::Value UIManagerBinding::getInspectorDataForInstance( + jsi::Runtime &runtime, + EventEmitter const &eventEmitter) const { + auto eventTarget = eventEmitter.eventTarget_; + EventEmitter::DispatchMutex().lock(); - if (uiManager_) { - uiManager_->uiManagerBinding_ = this; + if (!runtime.global().hasProperty(runtime, "__fbBatchedBridge") || + !eventTarget) { + return jsi::Value::undefined(); } + + eventTarget->retain(runtime); + auto instanceHandle = eventTarget->getInstanceHandle(runtime); + eventTarget->release(runtime); + EventEmitter::DispatchMutex().unlock(); + + if (instanceHandle.isUndefined()) { + return jsi::Value::undefined(); + } + + return callMethodOfModule( + runtime, + "ReactFabric", + "getInspectorDataForInstance", + {std::move(instanceHandle)}); } void UIManagerBinding::startSurface( jsi::Runtime &runtime, SurfaceId surfaceId, std::string const &moduleName, - folly::dynamic const &initalProps) const { + folly::dynamic const &initalProps, + DisplayMode displayMode) const { + SystraceSection s("UIManagerBinding::startSurface"); folly::dynamic parameters = folly::dynamic::object(); parameters["rootTag"] = surfaceId; parameters["initialProps"] = initalProps; @@ -98,31 +177,70 @@ void UIManagerBinding::startSurface( method.call( runtime, {jsi::String::createFromUtf8(runtime, moduleName), - jsi::valueFromDynamic(runtime, parameters)}); + jsi::valueFromDynamic(runtime, parameters), + jsi::Value(runtime, displayModeToInt(displayMode))}); } else { - auto module = getModule(runtime, "AppRegistry"); - auto method = module.getPropertyAsFunction(runtime, "runApplication"); + callMethodOfModule( + runtime, + "AppRegistry", + "runApplication", + {jsi::String::createFromUtf8(runtime, moduleName), + jsi::valueFromDynamic(runtime, parameters), + jsi::Value(runtime, displayModeToInt(displayMode))}); + } +} + +void UIManagerBinding::setSurfaceProps( + jsi::Runtime &runtime, + SurfaceId surfaceId, + std::string const &moduleName, + folly::dynamic const &initalProps, + DisplayMode displayMode) const { + SystraceSection s("UIManagerBinding::setSurfaceProps"); + folly::dynamic parameters = folly::dynamic::object(); + parameters["rootTag"] = surfaceId; + parameters["initialProps"] = initalProps; + parameters["fabric"] = true; - method.callWithThis( + if (moduleName.compare("LogBox") != 0 && + runtime.global().hasProperty(runtime, "RN$SurfaceRegistry")) { + auto registry = + runtime.global().getPropertyAsObject(runtime, "RN$SurfaceRegistry"); + auto method = registry.getPropertyAsFunction(runtime, "setSurfaceProps"); + + method.call( + runtime, + {jsi::String::createFromUtf8(runtime, moduleName), + jsi::valueFromDynamic(runtime, parameters), + jsi::Value(runtime, displayModeToInt(displayMode))}); + } else { + callMethodOfModule( runtime, - module, + "AppRegistry", + "setSurfaceProps", {jsi::String::createFromUtf8(runtime, moduleName), - jsi::valueFromDynamic(runtime, parameters)}); + jsi::valueFromDynamic(runtime, parameters), + jsi::Value(runtime, displayModeToInt(displayMode))}); } } void UIManagerBinding::stopSurface(jsi::Runtime &runtime, SurfaceId surfaceId) const { - if (runtime.global().hasProperty(runtime, "RN$stopSurface")) { - auto method = - runtime.global().getPropertyAsFunction(runtime, "RN$stopSurface"); - method.call(runtime, {jsi::Value{surfaceId}}); + auto global = runtime.global(); + if (global.hasProperty(runtime, "RN$Bridgeless")) { + if (!global.hasProperty(runtime, "RN$stopSurface")) { + // ReactFabric module has not been loaded yet; there's no surface to stop. + return; + } + // Bridgeless mode uses a custom JSI binding instead of callable module. + global.getPropertyAsFunction(runtime, "RN$stopSurface") + .call(runtime, {jsi::Value{surfaceId}}); } else { - auto module = getModule(runtime, "ReactFabric"); - auto method = - module.getPropertyAsFunction(runtime, "unmountComponentAtNode"); - - method.callWithThis(runtime, module, {jsi::Value{surfaceId}}); + callMethodOfModule( + runtime, + "ReactFabric", + "unmountComponentAtNode", + {jsi::Value{surfaceId}}); } } @@ -130,6 +248,7 @@ void UIManagerBinding::dispatchEvent( jsi::Runtime &runtime, EventTarget const *eventTarget, std::string const &type, + ReactEventPriority priority, ValueFactory const &payloadFactory) const { SystraceSection s("UIManagerBinding::dispatchEvent"); @@ -151,7 +270,7 @@ void UIManagerBinding::dispatchEvent( if (!payload.isObject()) { LOG(ERROR) << "payload for dispatchEvent is not an object: " << eventTarget->getTag(); } - assert(payload.isObject()); + react_native_assert(payload.isObject()); payload.asObject(runtime).setProperty(runtime, "target", eventTarget->getTag()); return instanceHandle; }() @@ -160,11 +279,13 @@ void UIManagerBinding::dispatchEvent( auto &eventHandlerWrapper = static_cast(*eventHandler_); + currentEventPriority_ = priority; eventHandlerWrapper.callback.call( runtime, {std::move(instanceHandle), jsi::String::createFromUtf8(runtime, type), std::move(payload)}); + currentEventPriority_ = ReactEventPriority::Default; } void UIManagerBinding::invalidate() const { @@ -175,6 +296,7 @@ jsi::Value UIManagerBinding::get( jsi::Runtime &runtime, jsi::PropNameID const &name) { auto methodName = name.utf8(runtime); + SystraceSection s("UIManagerBinding::get", "name", methodName); // Convert shared_ptr to a raw ptr // Why? Because: @@ -212,18 +334,24 @@ jsi::Value UIManagerBinding::get( name, 5, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { + auto eventTarget = + eventTargetFromValue(runtime, arguments[4], arguments[0]); + if (!eventTarget) { + react_native_assert(false); + return jsi::Value::undefined(); + } return valueFromShadowNode( runtime, uiManager->createNode( - tagFromValue(runtime, arguments[0]), + tagFromValue(arguments[0]), stringFromValue(runtime, arguments[1]), surfaceIdFromValue(runtime, arguments[2]), RawProps(runtime, arguments[3]), - eventTargetFromValue(runtime, arguments[4], arguments[0]))); + eventTarget)); }); } @@ -234,29 +362,30 @@ jsi::Value UIManagerBinding::get( name, 1, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0]))); }); } - if (methodName == "setJSResponder") { + if (methodName == "setIsJSResponder") { return jsi::Function::createFromHostFunction( runtime, name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { - uiManager->setJSResponder( + size_t count) noexcept -> jsi::Value { + uiManager->setIsJSResponder( shadowNodeFromValue(runtime, arguments[0]), - arguments[1].getBool()); + arguments[1].getBool(), + arguments[2].getBool()); return jsi::Value::undefined(); }); @@ -268,10 +397,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto node = shadowNodeFromValue(runtime, arguments[0]); auto locationX = (Float)arguments[1].getNumber(); auto locationY = (Float)arguments[2].getNumber(); @@ -292,22 +421,6 @@ jsi::Value UIManagerBinding::get( }); } - if (methodName == "clearJSResponder") { - return jsi::Function::createFromHostFunction( - runtime, - name, - 0, - [uiManager]( - jsi::Runtime & runtime, - jsi::Value const &thisValue, - jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { - uiManager->clearJSResponder(); - - return jsi::Value::undefined(); - }); - } - // Semantic: Clones the node with *same* props and *empty* children. if (methodName == "cloneNodeWithNewChildren") { return jsi::Function::createFromHostFunction( @@ -315,10 +428,10 @@ jsi::Value UIManagerBinding::get( name, 1, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -334,10 +447,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, @@ -355,10 +468,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, @@ -375,10 +488,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { uiManager->appendChild( shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1])); @@ -391,10 +504,10 @@ jsi::Value UIManagerBinding::get( runtime, name, 1, - [](jsi::Runtime & runtime, + [](jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto shadowNodeList = std::make_shared(SharedShadowNodeList({})); return valueFromShadowNodeList(runtime, shadowNodeList); @@ -406,10 +519,10 @@ jsi::Value UIManagerBinding::get( runtime, name, 2, - [](jsi::Runtime & runtime, + [](jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]); auto shadowNode = shadowNodeFromValue(runtime, arguments[1]); shadowNodeList->push_back(shadowNode); @@ -419,43 +532,45 @@ jsi::Value UIManagerBinding::get( if (methodName == "completeRoot") { if (uiManager->backgroundExecutor_) { + std::weak_ptr weakUIManager = uiManager_; // Enhanced version of the method that uses `backgroundExecutor` and // captures a shared pointer to `UIManager`. return jsi::Function::createFromHostFunction( runtime, name, 2, - [ uiManager, sharedUIManager = uiManager_ ]( - jsi::Runtime & runtime, + [weakUIManager, uiManager]( + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); - auto shadowNodeList = - shadowNodeListFromValue(runtime, arguments[1]); - - if (sharedUIManager->backgroundExecutor_) { - sharedUIManager->completeRootEventCounter_ += 1; - sharedUIManager->backgroundExecutor_( - [sharedUIManager, - surfaceId, - shadowNodeList, - eventCount = - sharedUIManager->completeRootEventCounter_.load()] { - auto shouldCancel = [eventCount, - sharedUIManager]() -> bool { - // If `eventCounter_` was incremented, another - // `completeSurface` call has been scheduled and current - // `completeSurface` should be cancelled. - return sharedUIManager->completeRootEventCounter_ > - eventCount; - }; - sharedUIManager->completeSurface( - surfaceId, shadowNodeList, {true, shouldCancel}); - }); - } else { - uiManager->completeSurface(surfaceId, shadowNodeList, {true, {}}); - } + auto weakShadowNodeList = + weakShadowNodeListFromValue(runtime, arguments[1]); + static std::atomic_uint_fast8_t completeRootEventCounter{0}; + static std::atomic_uint_fast32_t mostRecentSurfaceId{0}; + completeRootEventCounter += 1; + mostRecentSurfaceId = surfaceId; + uiManager->backgroundExecutor_( + [weakUIManager, + weakShadowNodeList, + surfaceId, + eventCount = completeRootEventCounter.load()] { + auto shouldYield = [=]() -> bool { + // If `completeRootEventCounter` was incremented, another + // `completeSurface` call has been scheduled and current + // `completeSurface` should yield to it. + return completeRootEventCounter > eventCount && + mostRecentSurfaceId == surfaceId; + }; + auto shadowNodeList = + shadowNodeListFromWeakList(weakShadowNodeList); + auto strongUIManager = weakUIManager.lock(); + if (shadowNodeList && strongUIManager) { + strongUIManager->completeSurface( + surfaceId, shadowNodeList, {true, shouldYield}); + } + }); return jsi::Value::undefined(); }); @@ -467,10 +582,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { uiManager->completeSurface( surfaceIdFromValue(runtime, arguments[0]), shadowNodeListFromValue(runtime, arguments[1]), @@ -487,10 +602,10 @@ jsi::Value UIManagerBinding::get( name, 1, [this]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto eventHandler = arguments[0].getObject(runtime).getFunction(runtime); eventHandler_ = @@ -505,10 +620,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -529,15 +644,17 @@ jsi::Value UIManagerBinding::get( name, 3, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { - uiManager->dispatchCommand( - shadowNodeFromValue(runtime, arguments[0]), - stringFromValue(runtime, arguments[1]), - commandArgsFromValue(runtime, arguments[2])); - + size_t count) noexcept -> jsi::Value { + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); + if (shadowNode) { + uiManager->dispatchCommand( + shadowNodeFromValue(runtime, arguments[0]), + stringFromValue(runtime, arguments[1]), + commandArgsFromValue(runtime, arguments[2])); + } return jsi::Value::undefined(); }); } @@ -549,10 +666,10 @@ jsi::Value UIManagerBinding::get( name, 4, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -585,14 +702,13 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto layoutMetrics = uiManager->getRelativeLayoutMetrics( - *shadowNodeFromValue(runtime, arguments[0]), - nullptr, - {/* .includeTransform = */ true}); + *shadowNode, nullptr, {/* .includeTransform = */ true}); auto onSuccessFunction = arguments[1].getObject(runtime).getFunction(runtime); @@ -600,12 +716,20 @@ jsi::Value UIManagerBinding::get( onSuccessFunction.call(runtime, {0, 0, 0, 0, 0, 0}); return jsi::Value::undefined(); } + auto newestCloneOfShadowNode = + uiManager->getNewestCloneOfShadowNode(*shadowNode); + + auto layoutableShadowNode = traitCast( + newestCloneOfShadowNode.get()); + Point originRelativeToParent = layoutableShadowNode + ? layoutableShadowNode->getLayoutMetrics().frame.origin + : Point(); auto frame = layoutMetrics.frame; onSuccessFunction.call( runtime, - {0, - 0, + {jsi::Value{runtime, (double)originRelativeToParent.x}, + jsi::Value{runtime, (double)originRelativeToParent.y}, jsi::Value{runtime, (double)frame.size.width}, jsi::Value{runtime, (double)frame.size.height}, jsi::Value{runtime, (double)frame.origin.x}, @@ -620,10 +744,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -649,16 +773,34 @@ jsi::Value UIManagerBinding::get( }); } + if (methodName == "sendAccessibilityEvent") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 2, + [uiManager]( + jsi::Runtime &runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { + uiManager->sendAccessibilityEvent( + shadowNodeFromValue(runtime, arguments[0]), + stringFromValue(runtime, arguments[1])); + + return jsi::Value::undefined(); + }); + } + if (methodName == "configureNextLayoutAnimation") { return jsi::Function::createFromHostFunction( runtime, name, 3, [uiManager]( - jsi::Runtime & runtime, + jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept->jsi::Value { + size_t count) noexcept -> jsi::Value { uiManager->configureNextLayoutAnimation( runtime, // TODO: pass in JSI value instead of folly::dynamic to RawValue @@ -669,8 +811,29 @@ jsi::Value UIManagerBinding::get( }); } + if (methodName == "unstable_getCurrentEventPriority") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 0, + [this]( + jsi::Runtime &, + jsi::Value const &, + jsi::Value const *, + size_t) noexcept -> jsi::Value { + return jsi::Value(serialize(currentEventPriority_)); + }); + } + + if (methodName == "unstable_DefaultEventPriority") { + return jsi::Value(serialize(ReactEventPriority::Default)); + } + + if (methodName == "unstable_DiscreteEventPriority") { + return jsi::Value(serialize(ReactEventPriority::Discrete)); + } + return jsi::Value::undefined(); } -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.h b/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.h index d5616310716a1..947e3ecf43e61 100644 --- a/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.h +++ b/android/ReactCommon/react/renderer/uimanager/UIManagerBinding.h @@ -7,14 +7,14 @@ #pragma once +#include #include #include #include #include #include -namespace facebook { -namespace react { +namespace facebook::react { /* * Exposes UIManager to JavaScript realm. @@ -29,7 +29,16 @@ class UIManagerBinding : public jsi::HostObject { * Thread synchronization must be enforced externally. */ static std::shared_ptr createAndInstallIfNeeded( - jsi::Runtime &runtime); + jsi::Runtime &runtime, + RuntimeExecutor const &runtimeExecutor); + + /* + * Returns a pointer to UIManagerBinding previously installed into a runtime. + * Thread synchronization must be enforced externally. + */ + static std::shared_ptr getBinding(jsi::Runtime &runtime); + + UIManagerBinding(RuntimeExecutor const &runtimeExecutor); ~UIManagerBinding(); @@ -48,7 +57,24 @@ class UIManagerBinding : public jsi::HostObject { jsi::Runtime &runtime, SurfaceId surfaceId, std::string const &moduleName, - folly::dynamic const &initalProps) const; + folly::dynamic const &initalProps, + DisplayMode displayMode) const; + + /* + * Updates the React Native Surface identified with surfaceId and moduleName + * with the given props. + * Thread synchronization must be enforced externally. + */ + void setSurfaceProps( + jsi::Runtime &runtime, + SurfaceId surfaceId, + std::string const &moduleName, + folly::dynamic const &props, + DisplayMode displayMode) const; + + jsi::Value getInspectorDataForInstance( + jsi::Runtime &runtime, + EventEmitter const &eventEmitter) const; /* * Stops React Native Surface with given id. @@ -64,6 +90,7 @@ class UIManagerBinding : public jsi::HostObject { jsi::Runtime &runtime, EventTarget const *eventTarget, std::string const &type, + ReactEventPriority priority, ValueFactory const &payloadFactory) const; /* @@ -83,7 +110,9 @@ class UIManagerBinding : public jsi::HostObject { private: std::shared_ptr uiManager_; std::unique_ptr eventHandler_; + mutable ReactEventPriority currentEventPriority_; + + RuntimeExecutor runtimeExecutor_; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h b/android/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h new file mode 100644 index 0000000000000..50fa5ebb66789 --- /dev/null +++ b/android/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react { + +class ShadowTree; +class UIManager; + +/* + * Implementing a commit hook allows to observe and alter Shadow Tree commits. + */ +class UIManagerCommitHook { + public: + /* + * Called right after the commit hook is registered or unregistered. + */ + virtual void commitHookWasRegistered( + UIManager const &uiManager) const noexcept = 0; + virtual void commitHookWasUnregistered( + UIManager const &uiManager) const noexcept = 0; + + /* + * Called right before a `ShadowTree` commits a new tree. + * The semantic of the method corresponds to a method of the same name + * from `ShadowTreeDelegate`. + */ + virtual RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const noexcept = 0; + + virtual ~UIManagerCommitHook() noexcept = default; +}; + +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h b/android/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h index ad0c9e27f5b54..91d8bf5510742 100644 --- a/android/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h +++ b/android/ReactCommon/react/renderer/uimanager/UIManagerDelegate.h @@ -11,8 +11,7 @@ #include #include -namespace facebook { -namespace react { +namespace facebook::react { /* * Abstract class for UIManager's delegate. @@ -31,8 +30,16 @@ class UIManagerDelegate { * might use this to optimistically allocate a new native view * instances. */ - virtual void uiManagerDidCreateShadowNode( - const ShadowNode::Shared &shadowNode) = 0; + virtual void uiManagerDidCreateShadowNode(const ShadowNode &shadowNode) = 0; + + /* + * Called each time when UIManager clones a Shadow Node. Receiver + * might use this to optimistically allocate a new native view + * instances. + */ + virtual void uiManagerDidCloneShadowNode( + const ShadowNode &oldShadowNode, + const ShadowNode &newShadowNode) = 0; /* * Called when UIManager wants to dispatch a command to the mounting layer. @@ -43,20 +50,23 @@ class UIManagerDelegate { folly::dynamic const args) = 0; /* - * Set JS responder for a view + * Called when UIManager wants to dispatch some accessibility event + * to the mounting layer. eventType is platform-specific and not all + * platforms will necessarily implement the same set of events. */ - virtual void uiManagerDidSetJSResponder( - SurfaceId surfaceId, - ShadowNode::Shared const &shadowView, - bool blockNativeResponder) = 0; + virtual void uiManagerDidSendAccessibilityEvent( + const ShadowNode::Shared &shadowNode, + std::string const &eventType) = 0; /* - * Clear the JSResponder for a view + * Set JS responder for a view. */ - virtual void uiManagerDidClearJSResponder() = 0; + virtual void uiManagerDidSetIsJSResponder( + ShadowNode::Shared const &shadowNode, + bool isJSResponder, + bool blockNativeResponder) = 0; virtual ~UIManagerDelegate() noexcept = default; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/renderer/uimanager/primitives.h b/android/ReactCommon/react/renderer/uimanager/primitives.h index 9fb2ea9278684..749fca7f28105 100644 --- a/android/ReactCommon/react/renderer/uimanager/primitives.h +++ b/android/ReactCommon/react/renderer/uimanager/primitives.h @@ -10,11 +10,11 @@ #include #include #include +#include #include #include -namespace facebook { -namespace react { +namespace facebook::react { using BackgroundExecutor = std::function &&callback)>; @@ -43,6 +43,10 @@ struct ShadowNodeListWrapper : public jsi::HostObject { inline static ShadowNode::Shared shadowNodeFromValue( jsi::Runtime &runtime, jsi::Value const &value) { + if (value.isNull()) { + return nullptr; + } + return value.getObject(runtime) .getHostObject(runtime) ->shadowNode; @@ -63,6 +67,34 @@ inline static SharedShadowNodeUnsharedList shadowNodeListFromValue( ->shadowNodeList; } +inline static ShadowNode::UnsharedListOfShared shadowNodeListFromWeakList( + ShadowNode::UnsharedListOfWeak const &weakShadowNodeList) { + auto result = std::make_shared(); + for (auto const &weakShadowNode : *weakShadowNodeList) { + auto sharedShadowNode = weakShadowNode.lock(); + if (!sharedShadowNode) { + return nullptr; + } + result->push_back(sharedShadowNode); + } + return result; +} + +inline static ShadowNode::UnsharedListOfWeak weakShadowNodeListFromValue( + jsi::Runtime &runtime, + jsi::Value const &value) { + auto shadowNodeList = value.getObject(runtime) + .getHostObject(runtime) + ->shadowNodeList; + + auto weakShadowNodeList = std::make_shared(); + for (auto const &shadowNode : *shadowNodeList) { + weakShadowNodeList->push_back(shadowNode); + } + + return weakShadowNodeList; +} + inline static jsi::Value valueFromShadowNodeList( jsi::Runtime &runtime, const SharedShadowNodeUnsharedList &shadowNodeList) { @@ -70,16 +102,20 @@ inline static jsi::Value valueFromShadowNodeList( runtime, std::make_unique(shadowNodeList)); } +inline static Tag tagFromValue(jsi::Value const &value) { + return (Tag)value.getNumber(); +} + inline static SharedEventTarget eventTargetFromValue( jsi::Runtime &runtime, jsi::Value const &eventTargetValue, jsi::Value const &tagValue) { + react_native_assert(!eventTargetValue.isNull()); + if (eventTargetValue.isNull()) { + return nullptr; + } return std::make_shared( - runtime, eventTargetValue, tagValue.getNumber()); -} - -inline static Tag tagFromValue(jsi::Runtime &runtime, jsi::Value const &value) { - return (Tag)value.getNumber(); + runtime, eventTargetValue, tagFromValue(tagValue)); } inline static SurfaceId surfaceIdFromValue( @@ -88,6 +124,19 @@ inline static SurfaceId surfaceIdFromValue( return (SurfaceId)value.getNumber(); } +inline static int displayModeToInt(DisplayMode const value) { + // the result of this method should be in sync with + // Libraries/ReactNative/DisplayMode.js + switch (value) { + case DisplayMode::Visible: + return 1; + case DisplayMode::Suspended: + return 2; + case DisplayMode::Hidden: + return 3; + } +} + inline static std::string stringFromValue( jsi::Runtime &runtime, jsi::Value const &value) { @@ -100,5 +149,4 @@ inline static folly::dynamic commandArgsFromValue( return jsi::dynamicFromValue(runtime, value); } -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/android/ReactCommon/react/test_utils/BUCK b/android/ReactCommon/react/test_utils/BUCK new file mode 100644 index 0000000000000..55a8cac9d0f5d --- /dev/null +++ b/android/ReactCommon/react/test_utils/BUCK @@ -0,0 +1,51 @@ +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "test_utils", + srcs = [], + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/test_utils", + ), + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_frameworks = ["Foundation"], + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + macosx_tests_override = [], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + tests = [], + visibility = ["PUBLIC"], + deps = [ + "//xplat/jsi:jsi", + react_native_xplat_target("better:better"), + react_native_xplat_target("react/debug:debug"), + ], + exported_deps = [ + "//xplat/third-party/gmock:gmock", + ], +) diff --git a/android/ReactCommon/react/renderer/mounting/tests/Entropy.h b/android/ReactCommon/react/test_utils/Entropy.h similarity index 96% rename from android/ReactCommon/react/renderer/mounting/tests/Entropy.h rename to android/ReactCommon/react/test_utils/Entropy.h index 54a87118bb179..7bbaf91012f2e 100644 --- a/android/ReactCommon/react/renderer/mounting/tests/Entropy.h +++ b/android/ReactCommon/react/test_utils/Entropy.h @@ -75,6 +75,10 @@ class Entropy final { result = generator() % 10000 < 10000 * ratio; } + void generateRandomValue(Generator &generator, int &result) const { + result = generator(); + } + void generateRandomValue(Generator &generator, int &result, int min, int max) const { std::uniform_int_distribution distribution(min, max); diff --git a/android/ReactCommon/react/test_utils/MockClock.h b/android/ReactCommon/react/test_utils/MockClock.h new file mode 100644 index 0000000000000..a5155207b2078 --- /dev/null +++ b/android/ReactCommon/react/test_utils/MockClock.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +class MockClock { + public: + typedef std::chrono:: + time_point + time_point; + + static time_point now() noexcept { + return time_; + } + + template + static void advance_by(const TDuration duration) { + time_ += duration; + } + + private: + static time_point time_; +}; diff --git a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.h b/android/ReactCommon/react/test_utils/MockSurfaceHandler.h similarity index 51% rename from android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.h rename to android/ReactCommon/react/test_utils/MockSurfaceHandler.h index fea36adf3f699..c6f678ce48487 100644 --- a/android/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.h +++ b/android/ReactCommon/react/test_utils/MockSurfaceHandler.h @@ -7,21 +7,18 @@ #pragma once -#include +#include + +#include namespace facebook { namespace react { -class PickerEventEmitter : public ViewEventEmitter { +class MockSurfaceHandler : public SurfaceHandler { public: - using ViewEventEmitter::ViewEventEmitter; - - struct PickerIOSChangeEvent { - std::string newValue; - int newIndex; - }; + MockSurfaceHandler() : SurfaceHandler("moduleName", 0){}; - void onChange(PickerIOSChangeEvent event) const; + MOCK_QUALIFIED_METHOD1(setDisplayMode, const noexcept, void(DisplayMode)); }; } // namespace react diff --git a/android/ReactCommon/react/renderer/mounting/tests/shadowTreeGeneration.h b/android/ReactCommon/react/test_utils/shadowTreeGeneration.h similarity index 74% rename from android/ReactCommon/react/renderer/mounting/tests/shadowTreeGeneration.h rename to android/ReactCommon/react/test_utils/shadowTreeGeneration.h index 8a2c326d157ef..f2897cb5e1879 100644 --- a/android/ReactCommon/react/renderer/mounting/tests/shadowTreeGeneration.h +++ b/android/ReactCommon/react/test_utils/shadowTreeGeneration.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -116,12 +117,16 @@ static inline ShadowNode::Unshared messWithChildren( std::make_shared(children)}); } -static inline ShadowNode::Unshared messWithLayotableOnlyFlag( +static inline ShadowNode::Unshared messWithLayoutableOnlyFlag( Entropy const &entropy, ShadowNode const &shadowNode) { auto oldProps = shadowNode.getProps(); + + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + auto newProps = shadowNode.getComponentDescriptor().cloneProps( - oldProps, RawProps(folly::dynamic::object())); + parserContext, oldProps, RawProps(folly::dynamic::object())); auto &viewProps = const_cast(static_cast(*newProps)); @@ -150,7 +155,7 @@ static inline ShadowNode::Unshared messWithLayotableOnlyFlag( } if (entropy.random(0.1)) { - viewProps.zIndex = entropy.random() ? 1 : 0; + viewProps.zIndex = entropy.random(); } if (entropy.random(0.1)) { @@ -163,6 +168,51 @@ static inline ShadowNode::Unshared messWithLayotableOnlyFlag( : Transform::Perspective(42); } + if (entropy.random(0.1)) { + viewProps.elevation = entropy.random() ? 1 : 0; + } + + return shadowNode.clone({newProps}); +} + +// Similar to `messWithLayoutableOnlyFlag` but has a 50/50 chance of flattening +// (or unflattening) a node's children. +static inline ShadowNode::Unshared messWithNodeFlattenednessFlags( + Entropy const &entropy, + ShadowNode const &shadowNode) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + + auto oldProps = shadowNode.getProps(); + auto newProps = shadowNode.getComponentDescriptor().cloneProps( + parserContext, oldProps, RawProps(folly::dynamic::object())); + + auto &viewProps = + const_cast(static_cast(*newProps)); + + if (entropy.random(0.5)) { + viewProps.nativeId = ""; + viewProps.collapsable = true; + viewProps.backgroundColor = SharedColor(); + viewProps.foregroundColor = SharedColor(); + viewProps.shadowColor = SharedColor(); + viewProps.accessible = false; + viewProps.zIndex = {}; + viewProps.pointerEvents = PointerEventsMode::Auto; + viewProps.transform = Transform::Identity(); + viewProps.elevation = 0; + } else { + viewProps.nativeId = "42"; + viewProps.backgroundColor = whiteColor(); + viewProps.foregroundColor = blackColor(); + viewProps.shadowColor = blackColor(); + viewProps.accessible = true; + viewProps.zIndex = {entropy.random()}; + viewProps.pointerEvents = PointerEventsMode::None; + viewProps.transform = Transform::Perspective(entropy.random()); + viewProps.elevation = entropy.random(); + } + return shadowNode.clone({newProps}); } @@ -189,9 +239,12 @@ static inline ShadowNode::Unshared messWithYogaStyles( } } + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + auto oldProps = shadowNode.getProps(); auto newProps = shadowNode.getComponentDescriptor().cloneProps( - oldProps, RawProps(dynamic)); + parserContext, oldProps, RawProps(dynamic)); return shadowNode.clone({newProps}); } @@ -221,8 +274,11 @@ static inline void alterShadowTree( static SharedViewProps generateDefaultProps( ComponentDescriptor const &componentDescriptor) { + ContextContainer contextContainer{}; + PropsParserContext parserContext{-1, contextContainer}; + return std::static_pointer_cast( - componentDescriptor.cloneProps(nullptr, RawProps{})); + componentDescriptor.cloneProps(parserContext, nullptr, RawProps{})); } static inline ShadowNode::Shared generateShadowNodeTree( @@ -250,8 +306,9 @@ static inline ShadowNode::Shared generateShadowNodeTree( auto family = componentDescriptor.createFamily( {generateReactTag(), SurfaceId(1), nullptr}, nullptr); return componentDescriptor.createShadowNode( - ShadowNodeFragment{generateDefaultProps(componentDescriptor), - std::make_shared(children)}, + ShadowNodeFragment{ + generateDefaultProps(componentDescriptor), + std::make_shared(children)}, family); } diff --git a/android/ReactCommon/react/utils/Android.mk b/android/ReactCommon/react/utils/Android.mk index 8a8cfca179728..471b15e6cff37 100644 --- a/android/ReactCommon/react/utils/Android.mk +++ b/android/ReactCommon/react/utils/Android.mk @@ -11,15 +11,20 @@ LOCAL_MODULE := react_utils LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) -LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../ LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := +LOCAL_SHARED_LIBRARIES := libreact_debug libreact_render_mapbuffer libglog libglog_init include $(BUILD_SHARED_LIBRARY) + +$(call import-module,react/debug) +$(call import-module,fbgloginit) +$(call import-module,glog) +$(call import-module,react/renderer/mapbuffer) diff --git a/android/ReactCommon/react/utils/BUCK b/android/ReactCommon/react/utils/BUCK index 9b525a1f1e037..6525d5b9b5ded 100644 --- a/android/ReactCommon/react/utils/BUCK +++ b/android/ReactCommon/react/utils/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -6,6 +5,8 @@ load( "CXX", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", + "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -33,11 +34,9 @@ rn_xplat_cxx_library( ], prefix = "react/utils", ), - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", + fbandroid_deps = [ + react_native_target("java/com/facebook/react/common/mapbuffer/jni:jni"), + react_native_xplat_target("react/renderer/mapbuffer:mapbuffer"), ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_frameworks = ["Foundation"], @@ -53,11 +52,13 @@ rn_xplat_cxx_library( tests = [], visibility = ["PUBLIC"], deps = [ + "//third-party/glog:glog", "//xplat/folly:container_evicting_cache_map", "//xplat/folly:headers_only", "//xplat/folly:memory", "//xplat/folly:molly", "//xplat/jsi:jsi", react_native_xplat_target("better:better"), + react_native_xplat_target("react/debug:debug"), ], ) diff --git a/android/ReactCommon/react/utils/CalledOnceMovableOnlyFunction.h b/android/ReactCommon/react/utils/CalledOnceMovableOnlyFunction.h index b6d31707d139c..4d4d2c0d056d9 100644 --- a/android/ReactCommon/react/utils/CalledOnceMovableOnlyFunction.h +++ b/android/ReactCommon/react/utils/CalledOnceMovableOnlyFunction.h @@ -7,6 +7,8 @@ #include +#include + namespace facebook { namespace react { @@ -31,7 +33,7 @@ class CalledOnceMovableOnlyFunction { } ~CalledOnceMovableOnlyFunction() { - assert( + react_native_assert( (wasCalled_ || wasMovedFrom_) && "`CalledOnceMovableOnlyFunction` is destroyed before being called."); } @@ -57,7 +59,7 @@ class CalledOnceMovableOnlyFunction { CalledOnceMovableOnlyFunction &operator=( CalledOnceMovableOnlyFunction &&other) noexcept { - assert( + react_native_assert( (wasCalled_ || wasMovedFrom_) && "`CalledOnceMovableOnlyFunction` is re-assigned before being called."); wasCalled_ = false; @@ -71,10 +73,10 @@ class CalledOnceMovableOnlyFunction { * Callable. */ ReturnT operator()(ArgumentT... args) { - assert( + react_native_assert( !wasMovedFrom_ && "`CalledOnceMovableOnlyFunction` is called after being moved from."); - assert( + react_native_assert( !wasCalled_ && "`CalledOnceMovableOnlyFunction` is called more than once."); diff --git a/android/ReactCommon/react/utils/ContextContainer.h b/android/ReactCommon/react/utils/ContextContainer.h index a8d0965566099..1431fe1af8319 100644 --- a/android/ReactCommon/react/utils/ContextContainer.h +++ b/android/ReactCommon/react/utils/ContextContainer.h @@ -15,6 +15,9 @@ #include #include +#include +#include + namespace facebook { namespace react { @@ -42,10 +45,6 @@ class ContextContainer final { std::unique_lock lock(mutex_); instances_.insert({key, std::make_shared(instance)}); - -#ifndef NDEBUG - typeNames_.insert({key, typeid(T).name()}); -#endif } /* @@ -56,10 +55,6 @@ class ContextContainer final { std::unique_lock lock(mutex_); instances_.erase(key); - -#ifndef NDEBUG - typeNames_.erase(key); -#endif } /* @@ -73,11 +68,6 @@ class ContextContainer final { for (auto const &pair : contextContainer.instances_) { instances_.erase(pair.first); instances_.insert(pair); -#ifndef NDEBUG - typeNames_.erase(pair.first); - typeNames_.insert( - {pair.first, contextContainer.typeNames_.at(pair.first)}); -#endif } } @@ -90,12 +80,9 @@ class ContextContainer final { T at(std::string const &key) const { std::shared_lock lock(mutex_); - assert( + react_native_assert( instances_.find(key) != instances_.end() && "ContextContainer doesn't have an instance for given key."); - assert( - typeNames_.at(key) == typeid(T).name() && - "ContextContainer stores an instance of different type for given key."); return *std::static_pointer_cast(instances_.at(key)); } @@ -113,10 +100,6 @@ class ContextContainer final { return {}; } - assert( - typeNames_.at(key) == typeid(T).name() && - "ContextContainer stores an instance of different type for given key."); - return *std::static_pointer_cast(iterator->second); } @@ -124,9 +107,6 @@ class ContextContainer final { mutable better::shared_mutex mutex_; // Protected by mutex_`. mutable better::map> instances_; -#ifndef NDEBUG - mutable better::map typeNames_; -#endif }; } // namespace react diff --git a/android/ReactCommon/react/utils/LayoutManager.h b/android/ReactCommon/react/utils/LayoutManager.h new file mode 100644 index 0000000000000..bc901971a3cbb --- /dev/null +++ b/android/ReactCommon/react/utils/LayoutManager.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#ifdef ANDROID +#include +#include +#include +#endif +#include + +namespace facebook { +namespace react { + +#ifdef ANDROID + +using namespace facebook::jni; + +Size measureAndroidComponent( + const ContextContainer::Shared &contextContainer, + Tag rootTag, + std::string componentName, + folly::dynamic localData, + folly::dynamic props, + folly::dynamic state, + float minWidth, + float maxWidth, + float minHeight, + float maxHeight, + jfloatArray attachmentPositions) { + const jni::global_ref &fabricUIManager = + contextContainer->at>("FabricUIManager"); + + static auto measure = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measure"); + + auto componentNameRef = make_jstring(componentName); + local_ref localDataRNM = + ReadableNativeMap::newObjectCxxArgs(localData); + local_ref propsRNM = + ReadableNativeMap::newObjectCxxArgs(props); + local_ref stateRNM = + ReadableNativeMap::newObjectCxxArgs(state); + + local_ref localDataRM = + make_local(reinterpret_cast(localDataRNM.get())); + local_ref propsRM = + make_local(reinterpret_cast(propsRNM.get())); + local_ref stateRM = + make_local(reinterpret_cast(stateRNM.get())); + + auto size = yogaMeassureToSize(measure( + fabricUIManager, + rootTag, + componentNameRef.get(), + localDataRM.get(), + propsRM.get(), + stateRM.get(), + minWidth, + maxWidth, + minHeight, + maxHeight, + attachmentPositions)); + + // Explicitly release smart pointers to free up space faster in JNI tables + componentNameRef.reset(); + localDataRM.reset(); + localDataRNM.reset(); + propsRM.reset(); + propsRNM.reset(); + stateRM.reset(); + stateRNM.reset(); + + return size; +} + +Size measureAndroidComponentMapBuffer( + const ContextContainer::Shared &contextContainer, + Tag rootTag, + std::string componentName, + MapBuffer &localData, + MapBuffer &props, + float minWidth, + float maxWidth, + float minHeight, + float maxHeight, + jfloatArray attachmentPositions) { + const jni::global_ref &fabricUIManager = + contextContainer->at>("FabricUIManager"); + auto componentNameRef = make_jstring(componentName); + + static auto measure = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measureMapBuffer"); + + auto localDataMap = + ReadableMapBuffer::createWithContents(std::move(localData)); + auto propsMap = ReadableMapBuffer::createWithContents(std::move(props)); + + auto size = yogaMeassureToSize(measure( + fabricUIManager, + rootTag, + componentNameRef.get(), + localDataMap.get(), + propsMap.get(), + minWidth, + maxWidth, + minHeight, + maxHeight, + attachmentPositions)); + + // Explicitly release smart pointers to free up space faster in JNI tables + componentNameRef.reset(); + localDataMap.reset(); + propsMap.reset(); + return size; +} + +#endif + +} // namespace react +} // namespace facebook diff --git a/android/ReactCommon/react/utils/ManagedObjectWrapper.h b/android/ReactCommon/react/utils/ManagedObjectWrapper.h index 623158015740d..f91cd3e99d81d 100644 --- a/android/ReactCommon/react/utils/ManagedObjectWrapper.h +++ b/android/ReactCommon/react/utils/ManagedObjectWrapper.h @@ -7,6 +7,8 @@ #pragma once +#include + #if defined(__OBJC__) && defined(__cplusplus) #if TARGET_OS_MAC && TARGET_OS_IPHONE @@ -65,7 +67,7 @@ inline std::shared_ptr wrapManagedObjectWeakly(id object) noexcept inline id unwrapManagedObjectWeakly(std::shared_ptr const &object) noexcept { RCTInternalGenericWeakWrapper *weakWrapper = (RCTInternalGenericWeakWrapper *)unwrapManagedObject(object); - assert(weakWrapper && "`RCTInternalGenericWeakWrapper` instance must not be `nil`."); + react_native_assert(weakWrapper && "`RCTInternalGenericWeakWrapper` instance must not be `nil`."); return weakWrapper.object; } diff --git a/android/ReactCommon/react/utils/RunLoopObserver.cpp b/android/ReactCommon/react/utils/RunLoopObserver.cpp index bca81b6664d7b..d24c3ede72bb1 100644 --- a/android/ReactCommon/react/utils/RunLoopObserver.cpp +++ b/android/ReactCommon/react/utils/RunLoopObserver.cpp @@ -7,7 +7,7 @@ #include "RunLoopObserver.h" -#include +#include namespace facebook { namespace react { @@ -19,8 +19,9 @@ RunLoopObserver::RunLoopObserver( void RunLoopObserver::setDelegate(Delegate const *delegate) const noexcept { // We need these constraints to ensure basic thread-safety. - assert(delegate && "A delegate must not be `nullptr`."); - assert(!delegate_ && "`RunLoopObserver::setDelegate` must be called once."); + react_native_assert(delegate && "A delegate must not be `nullptr`."); + react_native_assert( + !delegate_ && "`RunLoopObserver::setDelegate` must be called once."); delegate_ = delegate; } @@ -47,7 +48,7 @@ void RunLoopObserver::activityDidChange(Activity activity) const noexcept { return; } - assert( + react_native_assert( !owner_.expired() && "`owner_` is null. The caller must `lock` the owner and check it for being not null."); diff --git a/android/ReactCommon/reactperflogger/Android.mk b/android/ReactCommon/reactperflogger/Android.mk index f2c15a8027e32..a65bfd18ee02f 100644 --- a/android/ReactCommon/reactperflogger/Android.mk +++ b/android/ReactCommon/reactperflogger/Android.mk @@ -13,7 +13,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/reactperflogger # Header search path for modules that depend on this module LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall # Name of this module. LOCAL_MODULE := reactperflogger diff --git a/android/ReactCommon/reactperflogger/BUCK b/android/ReactCommon/reactperflogger/BUCK index 9724f14427235..d1a06c09571fb 100644 --- a/android/ReactCommon/reactperflogger/BUCK +++ b/android/ReactCommon/reactperflogger/BUCK @@ -9,10 +9,6 @@ rn_xplat_cxx_library( "reactperflogger/NativeModulePerfLogger.h": "reactperflogger/NativeModulePerfLogger.h", }, compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", "-Wno-global-constructors", ], labels = ["supermodule:xplat/default/public.react_native.infra"], diff --git a/android/ReactCommon/reactperflogger/React-perflogger.podspec b/android/ReactCommon/reactperflogger/React-perflogger.podspec index b620cbf0e270b..2be907ca3ae54 100644 --- a/android/ReactCommon/reactperflogger/React-perflogger.podspec +++ b/android/ReactCommon/reactperflogger/React-perflogger.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "reactperflogger" diff --git a/android/ReactCommon/runtimeexecutor/Android.mk b/android/ReactCommon/runtimeexecutor/Android.mk index b22071ecdab70..6b39530804dfa 100644 --- a/android/ReactCommon/runtimeexecutor/Android.mk +++ b/android/ReactCommon/runtimeexecutor/Android.mk @@ -14,6 +14,6 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall include $(BUILD_STATIC_LIBRARY) diff --git a/android/ReactCommon/runtimeexecutor/BUCK b/android/ReactCommon/runtimeexecutor/BUCK index 08ee7f4c23272..b8e04dfaf7200 100644 --- a/android/ReactCommon/runtimeexecutor/BUCK +++ b/android/ReactCommon/runtimeexecutor/BUCK @@ -1,4 +1,3 @@ -load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") load( "//tools/build_defs/oss:rn_defs.bzl", "ANDROID", @@ -6,6 +5,7 @@ load( "CXX", "get_apple_compiler_flags", "get_apple_inspector_flags", + "get_preprocessor_flags_for_build_mode", "rn_xplat_cxx_library", ) @@ -28,12 +28,6 @@ rn_xplat_cxx_library( exported_headers = { "ReactCommon/RuntimeExecutor.h": "ReactCommon/RuntimeExecutor.h", }, - compiler_flags = [ - "-fexceptions", - "-frtti", - "-std=c++14", - "-Wall", - ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_frameworks = ["Foundation"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), diff --git a/android/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec b/android/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec index 152c1f639b205..fd11178e5ac1f 100644 --- a/android/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec +++ b/android/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec @@ -11,13 +11,13 @@ version = package['version'] source = { :git => 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "ReactCommon" diff --git a/android/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h b/android/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h index 90139a6a99cfe..3e06081e45a86 100644 --- a/android/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h +++ b/android/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h @@ -39,9 +39,9 @@ using RuntimeExecutor = inline static void executeAsynchronously( RuntimeExecutor const &runtimeExecutor, std::function &&callback) noexcept { - std::thread{[callback = std::move(callback), runtimeExecutor]() mutable { + std::thread([callback = std::move(callback), runtimeExecutor]() mutable { runtimeExecutor(std::move(callback)); - }}; + }).detach(); } /* @@ -113,5 +113,17 @@ inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK( mutex3.lock(); } +template +inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK( + RuntimeExecutor const &runtimeExecutor, + std::function &&callback) noexcept { + DataT data; + + executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor, + [&](jsi::Runtime &runtime) { data = callback(runtime); }); + + return data; +} } // namespace react } // namespace facebook diff --git a/android/ReactCommon/yoga/BUCK b/android/ReactCommon/yoga/BUCK index 13e624f86b7d6..c6e4116f0bac1 100644 --- a/android/ReactCommon/yoga/BUCK +++ b/android/ReactCommon/yoga/BUCK @@ -7,11 +7,7 @@ cxx_library( exported_headers = glob(["yoga/**/*.h"]), compiler_flags = [ "-fno-omit-frame-pointer", - "-fexceptions", - "-Wall", - "-Werror", - "-std=c++1y", - "-O3", + "-O3" ], force_static = True, visibility = ["PUBLIC"], diff --git a/android/ReactCommon/yoga/yoga.podspec b/android/ReactCommon/yoga/yoga.podspec index b723a3410018d..701e93301a68a 100644 --- a/android/ReactCommon/yoga/yoga.podspec +++ b/android/ReactCommon/yoga/yoga.podspec @@ -9,7 +9,7 @@ version = package['version'] source = { :git => ENV['INSTALL_YOGA_FROM_LOCATION'] || 'https://github.com/facebook/react-native.git' } if version == '1000.0.0' # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. - source[:commit] = `git rev-parse HEAD`.strip + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") else source[:tag] = "v#{version}" end @@ -43,7 +43,7 @@ Pod::Spec.new do |spec| ] # Pinning to the same version as React.podspec. - spec.platforms = { :ios => "10.0" } + spec.platforms = { :ios => "11.0" } # Set this environment variable when *not* using the `:path` option to install the pod. # E.g. when publishing this spec to a spec repo. diff --git a/android/ReactCommon/yoga/yoga/BitUtils.h b/android/ReactCommon/yoga/yoga/BitUtils.h index 1c32e9ecc3556..2161effc7bb6f 100644 --- a/android/ReactCommon/yoga/yoga/BitUtils.h +++ b/android/ReactCommon/yoga/yoga/BitUtils.h @@ -45,8 +45,9 @@ void setEnumData(uint32_t& flags, size_t index, int newValue) { template void setEnumData(uint8_t& flags, size_t index, int newValue) { - flags = (flags & ~mask(bitWidthFn(), index)) | - ((newValue << index) & (mask(bitWidthFn(), index))); + flags = (flags & ~static_cast(mask(bitWidthFn(), index))) | + ((newValue << index) & + (static_cast(mask(bitWidthFn(), index)))); } constexpr bool getBooleanData(int flags, size_t index) { diff --git a/android/ReactCommon/yoga/yoga/CompactValue.h b/android/ReactCommon/yoga/yoga/CompactValue.h index be933a16eab85..f398668e2e138 100644 --- a/android/ReactCommon/yoga/yoga/CompactValue.h +++ b/android/ReactCommon/yoga/yoga/CompactValue.h @@ -125,8 +125,8 @@ class YOGA_EXPORT CompactValue { data.repr &= ~PERCENT_BIT; data.repr += BIAS; - return YGValue{data.value, - payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint}; + return YGValue{ + data.value, payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint}; } bool isUndefined() const noexcept { diff --git a/android/ReactCommon/yoga/yoga/Utils.cpp b/android/ReactCommon/yoga/yoga/Utils.cpp index edb198d25441e..eaa74b0629ac2 100644 --- a/android/ReactCommon/yoga/yoga/Utils.cpp +++ b/android/ReactCommon/yoga/yoga/Utils.cpp @@ -55,7 +55,7 @@ bool YGFloatsEqual(const float a, const float b) { bool YGDoubleEqual(const double a, const double b) { if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { - return fabs(a - b) < 0.0001f; + return fabs(a - b) < 0.0001; } return yoga::isUndefined(a) && yoga::isUndefined(b); } diff --git a/android/ReactCommon/yoga/yoga/YGNode.cpp b/android/ReactCommon/yoga/yoga/YGNode.cpp index 1ee1bde626e8a..f4c14bf3d8c9b 100644 --- a/android/ReactCommon/yoga/yoga/YGNode.cpp +++ b/android/ReactCommon/yoga/yoga/YGNode.cpp @@ -50,89 +50,111 @@ void YGNode::print(void* printContext) { } } -YGFloatOptional YGNode::getLeadingPosition( - const YGFlexDirection axis, - const float axisSize) const { - if (YGFlexDirectionIsRow(axis)) { - auto leadingPosition = YGComputedEdgeValue( - style_.position(), YGEdgeStart, CompactValue::ofUndefined()); - if (!leadingPosition.isUndefined()) { - return YGResolveValue(leadingPosition, axisSize); - } +CompactValue YGNode::computeEdgeValueForRow( + const YGStyle::Edges& edges, + YGEdge rowEdge, + YGEdge edge, + CompactValue defaultValue) { + if (!edges[rowEdge].isUndefined()) { + return edges[rowEdge]; + } else if (!edges[edge].isUndefined()) { + return edges[edge]; + } else if (!edges[YGEdgeHorizontal].isUndefined()) { + return edges[YGEdgeHorizontal]; + } else if (!edges[YGEdgeAll].isUndefined()) { + return edges[YGEdgeAll]; + } else { + return defaultValue; } +} - auto leadingPosition = YGComputedEdgeValue( - style_.position(), leading[axis], CompactValue::ofUndefined()); +CompactValue YGNode::computeEdgeValueForColumn( + const YGStyle::Edges& edges, + YGEdge edge, + CompactValue defaultValue) { + if (!edges[edge].isUndefined()) { + return edges[edge]; + } else if (!edges[YGEdgeVertical].isUndefined()) { + return edges[YGEdgeVertical]; + } else if (!edges[YGEdgeAll].isUndefined()) { + return edges[YGEdgeAll]; + } else { + return defaultValue; + } +} - return leadingPosition.isUndefined() - ? YGFloatOptional{0} - : YGResolveValue(leadingPosition, axisSize); +YGFloatOptional YGNode::getLeadingPosition( + const YGFlexDirection axis, + const float axisSize) const { + auto leadingPosition = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.position(), + YGEdgeStart, + leading[axis], + CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.position(), leading[axis], CompactValue::ofZero()); + return YGResolveValue(leadingPosition, axisSize); } YGFloatOptional YGNode::getTrailingPosition( const YGFlexDirection axis, const float axisSize) const { - if (YGFlexDirectionIsRow(axis)) { - auto trailingPosition = YGComputedEdgeValue( - style_.position(), YGEdgeEnd, CompactValue::ofUndefined()); - if (!trailingPosition.isUndefined()) { - return YGResolveValue(trailingPosition, axisSize); - } - } - - auto trailingPosition = YGComputedEdgeValue( - style_.position(), trailing[axis], CompactValue::ofUndefined()); - - return trailingPosition.isUndefined() - ? YGFloatOptional{0} - : YGResolveValue(trailingPosition, axisSize); + auto trailingPosition = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.position(), + YGEdgeEnd, + trailing[axis], + CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.position(), trailing[axis], CompactValue::ofZero()); + return YGResolveValue(trailingPosition, axisSize); } bool YGNode::isLeadingPositionDefined(const YGFlexDirection axis) const { - return (YGFlexDirectionIsRow(axis) && - !YGComputedEdgeValue( - style_.position(), YGEdgeStart, CompactValue::ofUndefined()) - .isUndefined()) || - !YGComputedEdgeValue( - style_.position(), leading[axis], CompactValue::ofUndefined()) - .isUndefined(); + auto leadingPosition = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.position(), + YGEdgeStart, + leading[axis], + CompactValue::ofUndefined()) + : computeEdgeValueForColumn( + style_.position(), leading[axis], CompactValue::ofUndefined()); + return !leadingPosition.isUndefined(); } bool YGNode::isTrailingPosDefined(const YGFlexDirection axis) const { - return (YGFlexDirectionIsRow(axis) && - !YGComputedEdgeValue( - style_.position(), YGEdgeEnd, CompactValue::ofUndefined()) - .isUndefined()) || - !YGComputedEdgeValue( - style_.position(), trailing[axis], CompactValue::ofUndefined()) - .isUndefined(); + auto trailingPosition = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.position(), + YGEdgeEnd, + trailing[axis], + CompactValue::ofUndefined()) + : computeEdgeValueForColumn( + style_.position(), trailing[axis], CompactValue::ofUndefined()); + return !trailingPosition.isUndefined(); } YGFloatOptional YGNode::getLeadingMargin( const YGFlexDirection axis, const float widthSize) const { - if (YGFlexDirectionIsRow(axis) && - !style_.margin()[YGEdgeStart].isUndefined()) { - return YGResolveValueMargin(style_.margin()[YGEdgeStart], widthSize); - } - - return YGResolveValueMargin( - YGComputedEdgeValue( - style_.margin(), leading[axis], CompactValue::ofZero()), - widthSize); + auto leadingMargin = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.margin(), YGEdgeStart, leading[axis], CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.margin(), leading[axis], CompactValue::ofZero()); + return YGResolveValueMargin(leadingMargin, widthSize); } YGFloatOptional YGNode::getTrailingMargin( const YGFlexDirection axis, const float widthSize) const { - if (YGFlexDirectionIsRow(axis) && !style_.margin()[YGEdgeEnd].isUndefined()) { - return YGResolveValueMargin(style_.margin()[YGEdgeEnd], widthSize); - } - - return YGResolveValueMargin( - YGComputedEdgeValue( - style_.margin(), trailing[axis], CompactValue::ofZero()), - widthSize); + auto trailingMargin = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.margin(), YGEdgeEnd, trailing[axis], CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.margin(), trailing[axis], CompactValue::ofZero()); + return YGResolveValueMargin(trailingMargin, widthSize); } YGFloatOptional YGNode::getMarginForAxis( @@ -147,7 +169,6 @@ YGSize YGNode::measure( float height, YGMeasureMode heightMode, void* layoutContext) { - return facebook::yoga::detail::getBooleanData(flags, measureUsesContext_) ? measure_.withContext( this, width, widthMode, height, heightMode, layoutContext) @@ -448,68 +469,48 @@ bool YGNode::isNodeFlexible() { } float YGNode::getLeadingBorder(const YGFlexDirection axis) const { - YGValue leadingBorder; - if (YGFlexDirectionIsRow(axis) && - !style_.border()[YGEdgeStart].isUndefined()) { - leadingBorder = style_.border()[YGEdgeStart]; - if (leadingBorder.value >= 0) { - return leadingBorder.value; - } - } - - leadingBorder = YGComputedEdgeValue( - style_.border(), leading[axis], CompactValue::ofZero()); - return YGFloatMax(leadingBorder.value, 0.0f); + YGValue leadingBorder = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.border(), YGEdgeStart, leading[axis], CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.border(), leading[axis], CompactValue::ofZero()); + return fmaxf(leadingBorder.value, 0.0f); } -float YGNode::getTrailingBorder(const YGFlexDirection flexDirection) const { - YGValue trailingBorder; - if (YGFlexDirectionIsRow(flexDirection) && - !style_.border()[YGEdgeEnd].isUndefined()) { - trailingBorder = style_.border()[YGEdgeEnd]; - if (trailingBorder.value >= 0.0f) { - return trailingBorder.value; - } - } - - trailingBorder = YGComputedEdgeValue( - style_.border(), trailing[flexDirection], CompactValue::ofZero()); - return YGFloatMax(trailingBorder.value, 0.0f); +float YGNode::getTrailingBorder(const YGFlexDirection axis) const { + YGValue trailingBorder = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.border(), YGEdgeEnd, trailing[axis], CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.border(), trailing[axis], CompactValue::ofZero()); + return fmaxf(trailingBorder.value, 0.0f); } YGFloatOptional YGNode::getLeadingPadding( const YGFlexDirection axis, const float widthSize) const { - const YGFloatOptional paddingEdgeStart = - YGResolveValue(style_.padding()[YGEdgeStart], widthSize); - if (YGFlexDirectionIsRow(axis) && - !style_.padding()[YGEdgeStart].isUndefined() && - !paddingEdgeStart.isUndefined() && paddingEdgeStart.unwrap() >= 0.0f) { - return paddingEdgeStart; - } - - YGFloatOptional resolvedValue = YGResolveValue( - YGComputedEdgeValue( - style_.padding(), leading[axis], CompactValue::ofZero()), - widthSize); - return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); + auto leadingPadding = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.padding(), + YGEdgeStart, + leading[axis], + CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.padding(), leading[axis], CompactValue::ofZero()); + return YGFloatOptionalMax( + YGResolveValue(leadingPadding, widthSize), YGFloatOptional(0.0f)); } YGFloatOptional YGNode::getTrailingPadding( const YGFlexDirection axis, const float widthSize) const { - const YGFloatOptional paddingEdgeEnd = - YGResolveValue(style_.padding()[YGEdgeEnd], widthSize); - if (YGFlexDirectionIsRow(axis) && paddingEdgeEnd >= YGFloatOptional{0.0f}) { - return paddingEdgeEnd; - } - - YGFloatOptional resolvedValue = YGResolveValue( - YGComputedEdgeValue( - style_.padding(), trailing[axis], CompactValue::ofZero()), - widthSize); - - return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); + auto trailingPadding = YGFlexDirectionIsRow(axis) + ? computeEdgeValueForRow( + style_.padding(), YGEdgeEnd, trailing[axis], CompactValue::ofZero()) + : computeEdgeValueForColumn( + style_.padding(), trailing[axis], CompactValue::ofZero()); + return YGFloatOptionalMax( + YGResolveValue(trailingPadding, widthSize), YGFloatOptional(0.0f)); } YGFloatOptional YGNode::getLeadingPaddingAndBorder( diff --git a/android/ReactCommon/yoga/yoga/YGNode.h b/android/ReactCommon/yoga/yoga/YGNode.h index 63d98fe3a1a81..4b6e6277af8aa 100644 --- a/android/ReactCommon/yoga/yoga/YGNode.h +++ b/android/ReactCommon/yoga/yoga/YGNode.h @@ -193,6 +193,17 @@ struct YOGA_EXPORT YGNode { return resolvedDimensions_[index]; } + static CompactValue computeEdgeValueForColumn( + const YGStyle::Edges& edges, + YGEdge edge, + CompactValue defaultValue); + + static CompactValue computeEdgeValueForRow( + const YGStyle::Edges& edges, + YGEdge rowEdge, + YGEdge edge, + CompactValue defaultValue); + // Methods related to positions, margin, padding and border YGFloatOptional getLeadingPosition( const YGFlexDirection axis, diff --git a/android/ReactCommon/yoga/yoga/YGNodePrint.cpp b/android/ReactCommon/yoga/yoga/YGNodePrint.cpp index 26efa4858b688..72d147dbb7b29 100644 --- a/android/ReactCommon/yoga/yoga/YGNodePrint.cpp +++ b/android/ReactCommon/yoga/yoga/YGNodePrint.cpp @@ -104,10 +104,13 @@ static void appendEdgeIfNotUndefined( const string& str, const YGStyle::Edges& edges, const YGEdge edge) { - appendNumberIfNotUndefined( - base, - str, - YGComputedEdgeValue(edges, edge, detail::CompactValue::ofUndefined())); + // TODO: this doesn't take RTL / YGEdgeStart / YGEdgeEnd into account + auto value = (edge == YGEdgeLeft || edge == YGEdgeRight) + ? YGNode::computeEdgeValueForRow( + edges, edge, edge, detail::CompactValue::ofUndefined()) + : YGNode::computeEdgeValueForColumn( + edges, edge, detail::CompactValue::ofUndefined()); + appendNumberIfNotUndefined(base, str, value); } void YGNodeToString( diff --git a/android/ReactCommon/yoga/yoga/Yoga-internal.h b/android/ReactCommon/yoga/yoga/Yoga-internal.h index 1a22f24c9c147..acd173be83f2f 100644 --- a/android/ReactCommon/yoga/yoga/Yoga-internal.h +++ b/android/ReactCommon/yoga/yoga/Yoga-internal.h @@ -33,6 +33,10 @@ inline bool isUndefined(float value) { return std::isnan(value); } +inline bool isUndefined(double value) { + return std::isnan(value); +} + } // namespace yoga } // namespace facebook @@ -144,8 +148,3 @@ static const float kDefaultFlexShrink = 0.0f; static const float kWebDefaultFlexShrink = 1.0f; extern bool YGFloatsEqual(const float a, const float b); -extern facebook::yoga::detail::CompactValue YGComputedEdgeValue( - const facebook::yoga::detail::Values< - facebook::yoga::enums::count()>& edges, - YGEdge edge, - facebook::yoga::detail::CompactValue defaultValue); diff --git a/android/ReactCommon/yoga/yoga/Yoga.cpp b/android/ReactCommon/yoga/yoga/Yoga.cpp index 97e640754d89f..2c68674a74293 100644 --- a/android/ReactCommon/yoga/yoga/Yoga.cpp +++ b/android/ReactCommon/yoga/yoga/Yoga.cpp @@ -106,38 +106,12 @@ static int YGDefaultLog( #undef YG_UNUSED #endif -YOGA_EXPORT bool YGFloatIsUndefined(const float value) { +static inline bool YGDoubleIsUndefined(const double value) { return facebook::yoga::isUndefined(value); } -detail::CompactValue YGComputedEdgeValue( - const YGStyle::Edges& edges, - YGEdge edge, - detail::CompactValue defaultValue) { - if (!edges[edge].isUndefined()) { - return edges[edge]; - } - - if ((edge == YGEdgeTop || edge == YGEdgeBottom) && - !edges[YGEdgeVertical].isUndefined()) { - return edges[YGEdgeVertical]; - } - - if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || - edge == YGEdgeEnd) && - !edges[YGEdgeHorizontal].isUndefined()) { - return edges[YGEdgeHorizontal]; - } - - if (!edges[YGEdgeAll].isUndefined()) { - return edges[YGEdgeAll]; - } - - if (edge == YGEdgeStart || edge == YGEdgeEnd) { - return detail::CompactValue::ofUndefined(); - } - - return defaultValue; +YOGA_EXPORT bool YGFloatIsUndefined(const float value) { + return facebook::yoga::isUndefined(value); } YOGA_EXPORT void* YGNodeGetContext(YGNodeRef node) { @@ -1681,40 +1655,33 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions( availableHeight = YGUndefined; } - const float paddingAndBorderAxisRow = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth); - const float paddingAndBorderAxisColumn = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth); - const float marginAxisRow = - node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); - const float marginAxisColumn = - node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + const auto& padding = node->getLayout().padding; + const auto& border = node->getLayout().border; + const float paddingAndBorderAxisRow = padding[YGEdgeLeft] + + padding[YGEdgeRight] + border[YGEdgeLeft] + border[YGEdgeRight]; + const float paddingAndBorderAxisColumn = padding[YGEdgeTop] + + padding[YGEdgeBottom] + border[YGEdgeTop] + border[YGEdgeBottom]; // We want to make sure we don't call measure with negative size const float innerWidth = YGFloatIsUndefined(availableWidth) ? availableWidth - : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + : YGFloatMax(0, availableWidth - paddingAndBorderAxisRow); const float innerHeight = YGFloatIsUndefined(availableHeight) ? availableHeight - : YGFloatMax( - 0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); + : YGFloatMax(0, availableHeight - paddingAndBorderAxisColumn); if (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly) { // Don't bother sizing the text if both dimensions are already defined. node->setLayoutMeasuredDimension( YGNodeBoundAxis( - node, - YGFlexDirectionRow, - availableWidth - marginAxisRow, - ownerWidth, - ownerWidth), + node, YGFlexDirectionRow, availableWidth, ownerWidth, ownerWidth), YGDimensionWidth); node->setLayoutMeasuredDimension( YGNodeBoundAxis( node, YGFlexDirectionColumn, - availableHeight - marginAxisColumn, + availableHeight, ownerHeight, ownerWidth), YGDimensionHeight); @@ -1751,7 +1718,7 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions( (widthMeasureMode == YGMeasureModeUndefined || widthMeasureMode == YGMeasureModeAtMost) ? measuredSize.width + paddingAndBorderAxisRow - : availableWidth - marginAxisRow, + : availableWidth, ownerWidth, ownerWidth), YGDimensionWidth); @@ -1763,7 +1730,7 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions( (heightMeasureMode == YGMeasureModeUndefined || heightMeasureMode == YGMeasureModeAtMost) ? measuredSize.height + paddingAndBorderAxisColumn - : availableHeight - marginAxisColumn, + : availableHeight, ownerHeight, ownerWidth), YGDimensionHeight); @@ -1780,37 +1747,28 @@ static void YGNodeEmptyContainerSetMeasuredDimensions( const YGMeasureMode heightMeasureMode, const float ownerWidth, const float ownerHeight) { - const float paddingAndBorderAxisRow = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth); - const float paddingAndBorderAxisColumn = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth); - const float marginAxisRow = - node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); - const float marginAxisColumn = - node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); - + const auto& padding = node->getLayout().padding; + const auto& border = node->getLayout().border; + + float width = availableWidth; + if (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) { + width = padding[YGEdgeLeft] + padding[YGEdgeRight] + border[YGEdgeLeft] + + border[YGEdgeRight]; + } node->setLayoutMeasuredDimension( - YGNodeBoundAxis( - node, - YGFlexDirectionRow, - (widthMeasureMode == YGMeasureModeUndefined || - widthMeasureMode == YGMeasureModeAtMost) - ? paddingAndBorderAxisRow - : availableWidth - marginAxisRow, - ownerWidth, - ownerWidth), + YGNodeBoundAxis(node, YGFlexDirectionRow, width, ownerWidth, ownerWidth), YGDimensionWidth); + float height = availableHeight; + if (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) { + height = padding[YGEdgeTop] + padding[YGEdgeBottom] + border[YGEdgeTop] + + border[YGEdgeBottom]; + } node->setLayoutMeasuredDimension( YGNodeBoundAxis( - node, - YGFlexDirectionColumn, - (heightMeasureMode == YGMeasureModeUndefined || - heightMeasureMode == YGMeasureModeAtMost) - ? paddingAndBorderAxisColumn - : availableHeight - marginAxisColumn, - ownerHeight, - ownerWidth), + node, YGFlexDirectionColumn, height, ownerHeight, ownerWidth), YGDimensionHeight); } @@ -1828,11 +1786,6 @@ static bool YGNodeFixedSizeSetMeasuredDimensions( heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly)) { - auto marginAxisColumn = - node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); - auto marginAxisRow = - node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); - node->setLayoutMeasuredDimension( YGNodeBoundAxis( node, @@ -1841,7 +1794,7 @@ static bool YGNodeFixedSizeSetMeasuredDimensions( (widthMeasureMode == YGMeasureModeAtMost && availableWidth < 0.0f) ? 0.0f - : availableWidth - marginAxisRow, + : availableWidth, ownerWidth, ownerWidth), YGDimensionWidth); @@ -1854,7 +1807,7 @@ static bool YGNodeFixedSizeSetMeasuredDimensions( (heightMeasureMode == YGMeasureModeAtMost && availableHeight < 0.0f) ? 0.0f - : availableHeight - marginAxisColumn, + : availableHeight, ownerHeight, ownerWidth), YGDimensionHeight); @@ -1878,21 +1831,11 @@ static void YGZeroOutLayoutRecursivly( static float YGNodeCalculateAvailableInnerDim( const YGNodeConstRef node, - YGFlexDirection axis, - float availableDim, - float ownerDim, - float ownerDimForMarginPadding) { - YGFlexDirection direction = - YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn; - YGDimension dimension = - YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight; - - const float margin = - node->getMarginForAxis(direction, ownerDimForMarginPadding).unwrap(); - const float paddingAndBorder = - YGNodePaddingAndBorderForAxis(node, direction, ownerDimForMarginPadding); - - float availableInnerDim = availableDim - margin - paddingAndBorder; + const YGDimension dimension, + const float availableDim, + const float paddingAndBorder, + const float ownerDim) { + float availableInnerDim = availableDim - paddingAndBorder; // Max dimension overrides predefined dimension value; Min dimension in turn // overrides both of the above if (!YGFloatIsUndefined(availableInnerDim)) { @@ -2779,16 +2722,22 @@ static void YGNodelayoutImpl( const YGEdge startEdge = direction == YGDirectionLTR ? YGEdgeLeft : YGEdgeRight; const YGEdge endEdge = direction == YGDirectionLTR ? YGEdgeRight : YGEdgeLeft; - node->setLayoutMargin( - node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap(), startEdge); - node->setLayoutMargin( - node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap(), endEdge); - node->setLayoutMargin( - node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap(), - YGEdgeTop); - node->setLayoutMargin( - node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap(), - YGEdgeBottom); + + const float marginRowLeading = + node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap(); + node->setLayoutMargin(marginRowLeading, startEdge); + const float marginRowTrailing = + node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap(); + node->setLayoutMargin(marginRowTrailing, endEdge); + const float marginColumnLeading = + node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap(); + node->setLayoutMargin(marginColumnLeading, YGEdgeTop); + const float marginColumnTrailing = + node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap(); + node->setLayoutMargin(marginColumnTrailing, YGEdgeBottom); + + const float marginAxisRow = marginRowLeading + marginRowTrailing; + const float marginAxisColumn = marginColumnLeading + marginColumnTrailing; node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), startEdge); node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), endEdge); @@ -2811,8 +2760,8 @@ static void YGNodelayoutImpl( if (node->hasMeasureFunc()) { YGNodeWithMeasureFuncSetMeasuredDimensions( node, - availableWidth, - availableHeight, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, widthMeasureMode, heightMeasureMode, ownerWidth, @@ -2827,8 +2776,8 @@ static void YGNodelayoutImpl( if (childCount == 0) { YGNodeEmptyContainerSetMeasuredDimensions( node, - availableWidth, - availableHeight, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, widthMeasureMode, heightMeasureMode, ownerWidth, @@ -2841,8 +2790,8 @@ static void YGNodelayoutImpl( if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions( node, - availableWidth, - availableHeight, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, widthMeasureMode, heightMeasureMode, ownerWidth, @@ -2866,12 +2815,14 @@ static void YGNodelayoutImpl( const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth; - const float leadingPaddingAndBorderCross = - node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap(); const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth); + const float leadingPaddingAndBorderCross = + node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap(); + const float trailingPaddingAndBorderCross = + node->getTrailingPaddingAndBorder(crossAxis, ownerWidth).unwrap(); const float paddingAndBorderAxisCross = - YGNodePaddingAndBorderForAxis(node, crossAxis, ownerWidth); + leadingPaddingAndBorderCross + trailingPaddingAndBorderCross; YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; @@ -2883,35 +2834,20 @@ static void YGNodelayoutImpl( const float paddingAndBorderAxisColumn = isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; - const float marginAxisRow = - node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); - const float marginAxisColumn = - node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); - - const auto& minDimensions = node->getStyle().minDimensions(); - const auto& maxDimensions = node->getStyle().maxDimensions(); - const float minInnerWidth = - YGResolveValue(minDimensions[YGDimensionWidth], ownerWidth).unwrap() - - paddingAndBorderAxisRow; - const float maxInnerWidth = - YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth).unwrap() - - paddingAndBorderAxisRow; - const float minInnerHeight = - YGResolveValue(minDimensions[YGDimensionHeight], ownerHeight).unwrap() - - paddingAndBorderAxisColumn; - const float maxInnerHeight = - YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight).unwrap() - - paddingAndBorderAxisColumn; - - const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight; - const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight; - // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS float availableInnerWidth = YGNodeCalculateAvailableInnerDim( - node, YGFlexDirectionRow, availableWidth, ownerWidth, ownerWidth); + node, + YGDimensionWidth, + availableWidth - marginAxisRow, + paddingAndBorderAxisRow, + ownerWidth); float availableInnerHeight = YGNodeCalculateAvailableInnerDim( - node, YGFlexDirectionColumn, availableHeight, ownerHeight, ownerWidth); + node, + YGDimensionHeight, + availableHeight - marginAxisColumn, + paddingAndBorderAxisColumn, + ownerHeight); float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; @@ -2983,6 +2919,28 @@ static void YGNodelayoutImpl( // If we don't measure with exact main dimension we want to ensure we don't // violate min and max if (measureModeMainDim != YGMeasureModeExactly) { + const auto& minDimensions = node->getStyle().minDimensions(); + const auto& maxDimensions = node->getStyle().maxDimensions(); + const float minInnerWidth = + YGResolveValue(minDimensions[YGDimensionWidth], ownerWidth).unwrap() - + paddingAndBorderAxisRow; + const float maxInnerWidth = + YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth).unwrap() - + paddingAndBorderAxisRow; + const float minInnerHeight = + YGResolveValue(minDimensions[YGDimensionHeight], ownerHeight) + .unwrap() - + paddingAndBorderAxisColumn; + const float maxInnerHeight = + YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight) + .unwrap() - + paddingAndBorderAxisColumn; + + const float minInnerMainDim = + isMainAxisRow ? minInnerWidth : minInnerHeight; + const float maxInnerMainDim = + isMainAxisRow ? maxInnerWidth : maxInnerHeight; + if (!YGFloatIsUndefined(minInnerMainDim) && collectedFlexItemsValues.sizeConsumedOnCurrentLine < minInnerMainDim) { @@ -3531,8 +3489,8 @@ static void YGNodelayoutImpl( YGNodeBoundAxisWithinMinAndMax( node, crossAxis, - YGFloatOptional{totalLineCrossDim + - paddingAndBorderAxisCross}, + YGFloatOptional{ + totalLineCrossDim + paddingAndBorderAxisCross}, crossAxisownerSize) .unwrap()), paddingAndBorderAxisCross), @@ -3557,7 +3515,8 @@ static void YGNodelayoutImpl( if (performLayout) { // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN for (auto child : node->getChildren()) { - if (child->getStyle().positionType() != YGPositionTypeAbsolute) { + if (child->getStyle().display() == YGDisplayNone || + child->getStyle().positionType() != YGPositionTypeAbsolute) { continue; } YGNodeAbsoluteLayoutChild( @@ -3665,10 +3624,10 @@ YOGA_EXPORT float YGRoundValueToPixelGrid( const double pointScaleFactor, const bool forceCeil, const bool forceFloor) { - double scaledValue = ((double) value) * pointScaleFactor; + double scaledValue = value * pointScaleFactor; // We want to calculate `fractial` such that `floor(scaledValue) = scaledValue // - fractial`. - double fractial = fmod(scaledValue, 1.0f); + double fractial = fmod(scaledValue, 1.0); if (fractial < 0) { // This branch is for handling negative numbers for `value`. // @@ -3690,25 +3649,25 @@ YOGA_EXPORT float YGRoundValueToPixelGrid( if (YGDoubleEqual(fractial, 0)) { // First we check if the value is already rounded scaledValue = scaledValue - fractial; - } else if (YGDoubleEqual(fractial, 1.0f)) { - scaledValue = scaledValue - fractial + 1.0f; + } else if (YGDoubleEqual(fractial, 1.0)) { + scaledValue = scaledValue - fractial + 1.0; } else if (forceCeil) { // Next we check if we need to use forced rounding - scaledValue = scaledValue - fractial + 1.0f; + scaledValue = scaledValue - fractial + 1.0; } else if (forceFloor) { scaledValue = scaledValue - fractial; } else { // Finally we just round the value scaledValue = scaledValue - fractial + - (!YGFloatIsUndefined(fractial) && - (fractial > 0.5f || YGDoubleEqual(fractial, 0.5f)) - ? 1.0f - : 0.0f); + (!YGDoubleIsUndefined(fractial) && + (fractial > 0.5 || YGDoubleEqual(fractial, 0.5)) + ? 1.0 + : 0.0); } - return (YGFloatIsUndefined(scaledValue) || - YGFloatIsUndefined(pointScaleFactor)) + return (YGDoubleIsUndefined(scaledValue) || + YGDoubleIsUndefined(pointScaleFactor)) ? YGUndefined - : scaledValue / pointScaleFactor; + : (float) (scaledValue / pointScaleFactor); } YOGA_EXPORT bool YGNodeCanUseCachedMeasurement( @@ -4235,9 +4194,7 @@ YOGA_EXPORT void YGNodeCalculateLayoutWithContext( if (node->getConfig()->printTree) { YGNodePrint( node, - (YGPrintOptions)( - YGPrintOptionsLayout | YGPrintOptionsChildren | - YGPrintOptionsStyle)); + (YGPrintOptions) (YGPrintOptionsLayout | YGPrintOptionsChildren | YGPrintOptionsStyle)); } #endif } @@ -4297,9 +4254,7 @@ YOGA_EXPORT void YGNodeCalculateLayoutWithContext( if (nodeWithoutLegacyFlag->getConfig()->printTree) { YGNodePrint( nodeWithoutLegacyFlag, - (YGPrintOptions)( - YGPrintOptionsLayout | YGPrintOptionsChildren | - YGPrintOptionsStyle)); + (YGPrintOptions) (YGPrintOptionsLayout | YGPrintOptionsChildren | YGPrintOptionsStyle)); } #endif } diff --git a/android/ReactCommon/yoga/yoga/Yoga.h b/android/ReactCommon/yoga/yoga/Yoga.h index 87901a280ee84..86cd65e2f08f0 100644 --- a/android/ReactCommon/yoga/yoga/Yoga.h +++ b/android/ReactCommon/yoga/yoga/Yoga.h @@ -107,7 +107,7 @@ WIN_EXPORT void YGNodeMarkDirty(YGNodeRef node); // Marks the current node and all its descendants as dirty. // -// Intended to be used for Uoga benchmarks. Don't use in production, as calling +// Intended to be used for Yoga benchmarks. Don't use in production, as calling // `YGCalculateLayout` will cause the recalculation of each and every node. WIN_EXPORT void YGNodeMarkDirtyAndPropogateToDescendants(YGNodeRef node); diff --git a/android/build.gradle b/android/build.gradle index 6328d90db7330..e08e2eb9abb44 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -10,7 +10,7 @@ buildscript { buildToolsVersion = '30.0.2' supportLibVersion = '29.0.0' kotlinVersion = '1.6.10' - gradlePluginVersion = '4.1.2' + gradlePluginVersion = '4.2.2' gradleDownloadTaskVersion = '3.4.3' repositoryUrl = "file:${System.env.HOME}/.m2/repository/" } @@ -54,7 +54,13 @@ allprojects { url "$rootDir/versioned-abis/maven" } google() - mavenCentral() + mavenCentral { + // We don't want to fetch react-native from Maven Central as there are + // older versions over there. + content { + excludeGroup "com.facebook.react" + } + } jcenter() maven { // Local Maven repo containing AARs with JSC built for Android @@ -77,6 +83,13 @@ allprojects { mavenLocal() } + // used to override ndk path/version from env variables on CI + ext["ANDROID_NDK_PATH"] = null + if (System.getenv("LOCAL_ANDROID_NDK_VERSION") != null) { + setProperty("ANDROID_NDK_VERSION", System.getenv("LOCAL_ANDROID_NDK_VERSION")) + ext["ANDROID_NDK_PATH"] = System.getenv("ANDROID_NDK") + } + configurations.all { // WHEN_DISTRIBUTING_REMOVE_FROM_HERE resolutionStrategy.dependencySubstitution { diff --git a/android/expoview/build.gradle b/android/expoview/build.gradle index b26d81745e070..112d41a1b7cab 100644 --- a/android/expoview/build.gradle +++ b/android/expoview/build.gradle @@ -65,7 +65,6 @@ def CMAKE_OUTPUT_DIR = "$buildDir/reanimated-ndk/all" def RN_SO_DIR = "$reactNative/ReactAndroid/build/react-ndk/exported" def FBJNI_HEADERS_DIR = "$reactNative/ReactAndroid/src/main/jni/first-party/fbjni/headers" def FOR_HERMES = false -def CMAKE_TOOLCHAIN_FILE = "-DCMAKE_TOOLCHAIN_FILE=${getNdkBuildFullPath()}/../build/cmake/android.toolchain.cmake" def ABI_FILTERS = System.getenv("NDK_ABI_FILTERS") ?: 'arm64-v8a, armeabi-v7a, x86, x86_64' // WHEN_PREPARING_REANIMATED_REMOVE_TO_HERE @@ -100,7 +99,11 @@ repositories { android { compileSdkVersion safeExtGet("compileSdkVersion", 30) - ndkVersion "21.1.6352462" + ndkVersion ANDROID_NDK_VERSION + + if (ANDROID_NDK_PATH != null) { + ndkPath ANDROID_NDK_PATH + } compileOptions { sourceCompatibility = '1.8' @@ -133,7 +136,7 @@ android { arguments "-DANDROID_STL=c++_shared", "-DREACT_NATIVE_TARGET_VERSION=${REACT_VERSION}", "-DANDROID_TOOLCHAIN=clang", - "${CMAKE_TOOLCHAIN_FILE}", + "${getCmakeToolchainFile()}", "-DBOOST_VERSION=${BOOST_VERSION}", "-DFOR_HERMES=${FOR_HERMES}", "-DREACT_NATIVE_PATH=${REACT_NATIVE_PATH}", @@ -171,6 +174,7 @@ android { "**/libfbjni.so", "**/libfolly_json.so", "**/libhermes.so", + "**/libjsi.so", ] } @@ -494,6 +498,7 @@ task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { // Prepare glog sources to be compiled, this task will perform steps that normally should've been // executed by automake. This way we can avoid dependencies on make/automake task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) { + duplicatesStrategy("warn") from(dependenciesPath ?: tarTree(downloadGlog.dest)) from("$reactNativeThirdParty/glog/") include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h") @@ -582,39 +587,34 @@ def getNdkBuildName() { } def findNdkBuildFullPath() { + // android.ndkDirectory should return project.android.ndkVersion ndkDirectory + def ndkDir = android.ndkDirectory ? android.ndkDirectory.absolutePath : null + if (ndkDir) { + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + // we allow to provide full path to ndk-build tool if (hasProperty("ndk.command")) { return property("ndk.command") } // or just a path to the containing directory if (hasProperty("ndk.path")) { - def ndkDir = property("ndk.path") + ndkDir = property("ndk.path") return new File(ndkDir, getNdkBuildName()).getAbsolutePath() } + // @TODO ANDROID_NDK && ndk.dir is deprecated and will be removed in the future. if (System.getenv("ANDROID_NDK_HOME") != null) { - def ndkDir = System.getenv("ANDROID_NDK_HOME") + ndkDir = System.getenv("ANDROID_NDK_HOME") return new File(ndkDir, getNdkBuildName()).getAbsolutePath() } // Left for the legacy reasons, use the previous one. if (System.getenv("ANDROID_NDK") != null) { - def ndkDir = System.getenv("ANDROID_NDK") + ndkDir = System.getenv("ANDROID_NDK") return new File(ndkDir, getNdkBuildName()).getAbsolutePath() } - try { - def ndkDir = android.ndkDirectory.absolutePath - if (ndkDir) { - return new File(ndkDir, getNdkBuildName()).getAbsolutePath() - } - } catch (InvalidUserDataException e) { - throw new GradleScriptException( - "Default side-by-side NDK installation is not found.\n" + - "Set \$ANDROID_NDK_HOME environment variable correctly or setup ndk.dir in local.properties.", e - ) - } - return null } @@ -637,6 +637,10 @@ def getNdkBuildFullPath() { return ndkBuildFullPath } +def getCmakeToolchainFile() { + return "-DCMAKE_TOOLCHAIN_FILE=${getNdkBuildFullPath()}/../build/cmake/android.toolchain.cmake" +} + task prepareHermes() { def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") if (!hermesPackagePath) { diff --git a/android/expoview/src/main/java/host/exp/exponent/network/ExponentHttpClient.kt b/android/expoview/src/main/java/host/exp/exponent/network/ExponentHttpClient.kt index 2edbe16dca293..4d61989083b75 100644 --- a/android/expoview/src/main/java/host/exp/exponent/network/ExponentHttpClient.kt +++ b/android/expoview/src/main/java/host/exp/exponent/network/ExponentHttpClient.kt @@ -6,8 +6,10 @@ import host.exp.exponent.Constants import host.exp.exponent.analytics.Analytics import host.exp.exponent.analytics.EXL import okhttp3.* +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okio.BufferedSource -import okio.Okio +import okio.buffer +import okio.source import org.json.JSONException import org.json.JSONObject import java.io.FileNotFoundException @@ -41,7 +43,7 @@ class ExponentHttpClient( } fun callSafe(request: Request, callback: SafeCallback) { - val uri = request.url().toString() + val uri = request.url.toString() okHttpClientFactory.getNewClient().newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { tryForcedCachedResponse(uri, request, callback, null, e) @@ -60,7 +62,7 @@ class ExponentHttpClient( fun callDefaultCache(request: Request, callback: SafeCallback) { tryForcedCachedResponse( - request.url().toString(), + request.url.toString(), request, object : SafeCallback { override fun onFailure(e: IOException) { @@ -146,7 +148,7 @@ class ExponentHttpClient( .body( responseBodyForFile( embeddedResponse.responseFilePath, - MediaType.parse(embeddedResponse.mediaType) + embeddedResponse.mediaType.toMediaTypeOrNull() ) ) .build() @@ -174,8 +176,8 @@ class ExponentHttpClient( } val stream = context.assets.open(strippedAssetsPath) - val source = Okio.source(stream) - val buffer = Okio.buffer(source) + val source = stream.source() + val buffer = source.buffer() object : ResponseBody() { override fun contentType(): MediaType? { diff --git a/android/expoview/src/main/java/host/exp/exponent/network/OkHttpV1ExpoResponse.kt b/android/expoview/src/main/java/host/exp/exponent/network/OkHttpV1ExpoResponse.kt index c101bcc5ebbb0..02a850dee942f 100644 --- a/android/expoview/src/main/java/host/exp/exponent/network/OkHttpV1ExpoResponse.kt +++ b/android/expoview/src/main/java/host/exp/exponent/network/OkHttpV1ExpoResponse.kt @@ -23,11 +23,11 @@ class OkHttpV1ExpoResponse(private val okHttpResponse: Response) : ExpoResponse override val isSuccessful: Boolean = okHttpResponse.isSuccessful - override fun body(): ExpoBody = OkHttpV1ExpoBody(okHttpResponse.body()!!) + override fun body(): ExpoBody = OkHttpV1ExpoBody(okHttpResponse.body!!) - override fun code(): Int = okHttpResponse.code() + override fun code(): Int = okHttpResponse.code - override fun headers(): ExpoHeaders = OkHttpV1ExpoHeaders(okHttpResponse.headers()) + override fun headers(): ExpoHeaders = OkHttpV1ExpoHeaders(okHttpResponse.headers) - override fun networkResponse(): ExpoResponse? = okHttpResponse.networkResponse()?.let { OkHttpV1ExpoResponse(it) } + override fun networkResponse(): ExpoResponse? = okHttpResponse.networkResponse?.let { OkHttpV1ExpoResponse(it) } } diff --git a/android/expoview/src/main/java/host/exp/exponent/notifications/ExponentNotificationIntentService.kt b/android/expoview/src/main/java/host/exp/exponent/notifications/ExponentNotificationIntentService.kt index badf40c4bc110..6437b686ad148 100644 --- a/android/expoview/src/main/java/host/exp/exponent/notifications/ExponentNotificationIntentService.kt +++ b/android/expoview/src/main/java/host/exp/exponent/notifications/ExponentNotificationIntentService.kt @@ -12,8 +12,8 @@ import host.exp.exponent.network.ExpoResponse import host.exp.exponent.network.ExponentNetwork import host.exp.exponent.storage.ExponentSharedPreferences import host.exp.exponent.utils.AsyncCondition -import okhttp3.MediaType -import okhttp3.RequestBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -85,7 +85,7 @@ abstract class ExponentNotificationIntentService(name: String?) : IntentService( put("type", getServerType()) } - val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), params.toString()) + val body = params.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) val request = ExponentUrls.addExponentHeadersToUrl("https://exp.host/--/api/v2/push/updateDeviceToken") .header("Content-Type", "application/json") .post(body) diff --git a/android/expoview/src/main/java/host/exp/exponent/notifications/NotificationHelper.kt b/android/expoview/src/main/java/host/exp/exponent/notifications/NotificationHelper.kt index 750771df5dc66..f7432e4ce9af9 100644 --- a/android/expoview/src/main/java/host/exp/exponent/notifications/NotificationHelper.kt +++ b/android/expoview/src/main/java/host/exp/exponent/notifications/NotificationHelper.kt @@ -36,8 +36,8 @@ import host.exp.exponent.utils.AsyncCondition.AsyncConditionListener import host.exp.exponent.utils.ColorParser import host.exp.exponent.utils.JSONUtils.getJSONString import host.exp.expoview.R -import okhttp3.MediaType -import okhttp3.RequestBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -137,7 +137,7 @@ object NotificationHelper { } } - val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), params.toString()) + val body = params.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) val request = ExponentUrls.addExponentHeadersToUrl("https://exp.host/--/api/v2/push/getExpoPushToken") .header("Content-Type", "application/json") .post(body) diff --git a/android/expoview/src/main/java/host/exp/expoview/Exponent.kt b/android/expoview/src/main/java/host/exp/expoview/Exponent.kt index 5cdc5af334d11..990b60621d999 100644 --- a/android/expoview/src/main/java/host/exp/expoview/Exponent.kt +++ b/android/expoview/src/main/java/host/exp/expoview/Exponent.kt @@ -251,7 +251,7 @@ class Exponent private constructor(val context: Context, val application: Applic exponentNetwork.longTimeoutClient.apply { when { shouldForceCache -> tryForcedCachedResponse( - request.url().toString(), + request.url.toString(), request, callback, null, @@ -309,7 +309,7 @@ class Exponent private constructor(val context: Context, val application: Applic @Throws(IOException::class) override fun onResponse(call: Call, response: Response) { - val responseString = response.body()!!.string() + val responseString = response.body!!.string() if (responseString.contains(PACKAGER_RUNNING)) { runOnUiThread { callback.onSuccess() } } else { diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/reanimated/layoutReanimation/ReanimatedUIImplementation.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/reanimated/layoutReanimation/ReanimatedUIImplementation.java index 76a491f54ce11..cfae16c65a539 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/reanimated/layoutReanimation/ReanimatedUIImplementation.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/reanimated/layoutReanimation/ReanimatedUIImplementation.java @@ -10,7 +10,7 @@ public class ReanimatedUIImplementation extends UIImplementation { public ReanimatedUIImplementation( ReactApplicationContext reactContext, - UIManagerModule.ViewManagerResolver viewManagerResolver, + ViewManagerResolver viewManagerResolver, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) { this( diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/internal/DevMenuModule.kt b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/internal/DevMenuModule.kt index a9bc106dafdcf..ac493c728a263 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/internal/DevMenuModule.kt +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/internal/DevMenuModule.kt @@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.devsupport.DevInternalSettings -import com.facebook.react.devsupport.DevSupportManagerImpl +import com.facebook.react.devsupport.BridgeDevSupportManager import com.facebook.react.devsupport.HMRClient import expo.modules.manifests.core.Manifest import host.exp.exponent.di.NativeModuleDepsProvider @@ -186,12 +186,12 @@ class DevMenuModule(reactContext: ReactApplicationContext, val experiencePropert //region internals /** - * Returns versioned instance of [DevSupportManagerImpl], + * Returns versioned instance of [BridgeDevSupportManager], * or null if no activity is currently attached to react context. */ - private fun getDevSupportManager(): DevSupportManagerImpl? { + private fun getDevSupportManager(): BridgeDevSupportManager? { val activity = currentActivity as? ReactNativeActivity? - return activity?.devSupportManager?.get() as? DevSupportManagerImpl? + return activity?.devSupportManager?.get() as? BridgeDevSupportManager? } /** diff --git a/android/gradle.properties b/android/gradle.properties index 26f90cfb5b620..113c0753415d3 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -3,6 +3,8 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xmx9216M -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.configureondemand=true org.gradle.internal.repository.initial.backoff=1000 + +ANDROID_NDK_VERSION=21.4.7075529 android.useAndroidX=true android.enableJetifier=true android.useNewApkCreator=false diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c4a3a7162652..dff02fad93967 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 18fb2743087ea..2bfe629313de0 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -34,9 +34,7 @@ END UNCOMMENT WHEN DISTRIBUTING */ // WHEN_DISTRIBUTING_REMOVE_FROM_HERE -include ':packages:react-native-codegen:android' -project(':packages:react-native-codegen:android').projectDir = new File('../react-native-lab/react-native/packages/react-native-codegen/android') -includeBuild('../react-native-lab/react-native/packages/react-native-codegen/android') +includeBuild('../react-native-lab/react-native/packages/react-native-gradle-plugin/') include ':expoview' include ':tools' diff --git a/android/tools/build.gradle b/android/tools/build.gradle index c999c8c258ea2..5c8460fc80fa2 100644 --- a/android/tools/build.gradle +++ b/android/tools/build.gradle @@ -1,10 +1,10 @@ apply plugin: 'java' dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile group: 'com.github.javaparser', name: 'javaparser-core', version: '3.3.4' - compile group: 'commons-io', name: 'commons-io', version: '2.6' - compile group: 'org.json', name: 'json', version: '20170516' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation group: 'com.github.javaparser', name: 'javaparser-core', version: '3.3.4' + implementation group: 'commons-io', name: 'commons-io', version: '2.6' + implementation group: 'org.json', name: 'json', version: '20170516' } task execute(type: JavaExec) { diff --git a/android/tools/src/main/java/host/exp/exponent/tools/ReactAndroidCodeTransformer.java b/android/tools/src/main/java/host/exp/exponent/tools/ReactAndroidCodeTransformer.java index 3b38cf8618520..b5161fe5ddaeb 100644 --- a/android/tools/src/main/java/host/exp/exponent/tools/ReactAndroidCodeTransformer.java +++ b/android/tools/src/main/java/host/exp/exponent/tools/ReactAndroidCodeTransformer.java @@ -196,6 +196,8 @@ public Node visit(String methodName, MethodDeclaration n) { return n; } }); + FILES_TO_MODIFY.put("devsupport/BridgeDevSupportManager.java", null); + FILES_TO_MODIFY.put("modules/core/ExceptionsManagerModule.java", new MethodVisitor() { @Override @@ -309,17 +311,17 @@ public static void main(final String[] args) throws IOException { FileUtils.copyDirectory(new File(projectRoot + REACT_COMMON_SOURCE_ROOT), reactCommonDestRoot); FileUtils.copyDirectory(new File(projectRoot + REACT_ANDROID_SOURCE_ROOT), reactAndroidDestRoot); - // Update release.gradle - replaceInFile(new File(projectRoot + REACT_ANDROID_DEST_ROOT + "/release.gradle"), - "'https://oss.sonatype.org/service/local/staging/deploy/maven2/'", - "\"file:${System.env.HOME}/.m2/repository/\""); + // Update maven publish information + replaceInFile(new File(projectRoot + REACT_ANDROID_DEST_ROOT + "/build.gradle"), + "def AAR_OUTPUT_URL = \"file://${projectDir}/../android\"", + "def AAR_OUTPUT_URL = \"file:${System.env.HOME}/.m2/repository\""); - replaceInFile(new File(projectRoot + REACT_ANDROID_DEST_ROOT + "/release.gradle"), + replaceInFile(new File(projectRoot + REACT_ANDROID_DEST_ROOT + "/build.gradle"), "group = GROUP", "group = 'com.facebook.react'"); // This version also gets updated in android-tasks.js - replaceInFile(new File(projectRoot + REACT_ANDROID_DEST_ROOT + "/release.gradle"), + replaceInFile(new File(projectRoot + REACT_ANDROID_DEST_ROOT + "/build.gradle"), "version = VERSION_NAME", "version = '" + sdkVersion + "'"); @@ -400,6 +402,8 @@ public Node visit(final ConstructorDeclaration n, final Void arg) { switch (name) { case "NetworkingModule": return networkingModuleConstructor(n); + case "BridgeDevSupportManager": + return bridgeDevSupportManagerConstructor(n); } return n; @@ -627,6 +631,25 @@ public Statement map(Statement statement) { }); } + // Remove some custom dev options. unlike `showDevOptionsDialog`, this happens in constructor. + private static Node bridgeDevSupportManagerConstructor(final ConstructorDeclaration n) { + return mapBlockStatement(n, new StatementMapper() { + @Override + public Statement map(Statement statement) { + if (statement instanceof LabeledStmt) { + LabeledStmt labeledStmt = (LabeledStmt) statement; + if ("expo_transformer_remove".equals(labeledStmt.getLabel().getIdentifier())) { + Statement emptyStatement = new EmptyStmt(); + emptyStatement.setLineComment(" code removed by ReactAndroidCodeTransformer"); + return emptyStatement; + } + } + + return statement; + } + }); + } + private static Node wrapInTryCatch(final MethodDeclaration n) { n.getBody().ifPresent(body -> { Statement tryCatch = getTryCatch(body); diff --git a/android/versioned-abis/expoview-abi43_0_0/src/main/java/abi43_0_0/expo/modules/filesystem/CountingRequestBody.kt b/android/versioned-abis/expoview-abi43_0_0/src/main/java/abi43_0_0/expo/modules/filesystem/CountingRequestBody.kt index 6c4589c5d40af..3f46449e1b836 100644 --- a/android/versioned-abis/expoview-abi43_0_0/src/main/java/abi43_0_0/expo/modules/filesystem/CountingRequestBody.kt +++ b/android/versioned-abis/expoview-abi43_0_0/src/main/java/abi43_0_0/expo/modules/filesystem/CountingRequestBody.kt @@ -1,11 +1,7 @@ package abi43_0_0.expo.modules.filesystem import okhttp3.RequestBody -import okio.Buffer -import okio.BufferedSink -import okio.ForwardingSink -import okio.Okio -import okio.Sink +import okio.* import java.io.IOException @FunctionalInterface @@ -44,7 +40,7 @@ class CountingRequestBody( override fun writeTo(sink: BufferedSink) { val countingSink = CountingSink(sink, this, progressListener) - val bufferedSink = Okio.buffer(countingSink) + val bufferedSink = countingSink.buffer() requestBody.writeTo(bufferedSink) bufferedSink.flush() } diff --git a/android/versioned-abis/expoview-abi44_0_0/src/main/java/abi44_0_0/expo/modules/filesystem/FileSystemModule.kt b/android/versioned-abis/expoview-abi44_0_0/src/main/java/abi44_0_0/expo/modules/filesystem/FileSystemModule.kt index 5e64b1d4639bb..b3f7c3e0b184d 100644 --- a/android/versioned-abis/expoview-abi44_0_0/src/main/java/abi44_0_0/expo/modules/filesystem/FileSystemModule.kt +++ b/android/versioned-abis/expoview-abi44_0_0/src/main/java/abi44_0_0/expo/modules/filesystem/FileSystemModule.kt @@ -56,18 +56,15 @@ import okhttp3.Callback import okhttp3.Headers import okhttp3.JavaNetCookieJar import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.Response import okhttp3.ResponseBody - -import okio.Buffer -import okio.BufferedSource -import okio.ForwardingSource -import okio.Okio -import okio.Source +import okio.* import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.digest.DigestUtils @@ -766,7 +763,7 @@ open class FileSystemModule( } val body = createRequestBody(options, decorator, fileUri.toFile()) - return requestBuilder.method(method, body).build() + return method?.let { requestBuilder.method(method, body).build() } } catch (e: Exception) { e.message?.let { Log.e(TAG, it) } promise.reject(e) @@ -791,7 +788,7 @@ open class FileSystemModule( } ?: URLConnection.guessContentTypeFromName(file.name) val fieldName = options["fieldName"]?.let { it as String } ?: file.name - bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(MediaType.parse(mimeType), file))) + bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(file.asRequestBody(mimeType.toMediaTypeOrNull()))) bodyBuilder.build() } else -> { @@ -816,9 +813,9 @@ open class FileSystemModule( override fun onResponse(call: Call, response: Response) { val result = Bundle().apply { - putString("body", response.body()?.string()) - putInt("status", response.code()) - putBundle("headers", translateHeaders(response.headers())) + putString("body", response.body?.string()) + putInt("status", response.code) + putBundle("headers", translateHeaders(response.headers)) } response.close() promise.resolve(result) @@ -866,7 +863,7 @@ open class FileSystemModule( taskHandlers[uuid] = TaskHandler(call) call.enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { - if (call.isCanceled) { + if (call.isCanceled()) { promise.resolve(null) return } @@ -876,11 +873,11 @@ open class FileSystemModule( override fun onResponse(call: Call, response: Response) { val result = Bundle() - val body = response.body() + val body = response.body result.apply { putString("body", body?.string()) - putInt("status", response.code()) - putBundle("headers", translateHeaders(response.headers())) + putInt("status", response.code) + putBundle("headers", translateHeaders(response.headers)) } response.close() promise.resolve(result) @@ -900,10 +897,10 @@ open class FileSystemModule( val resources = context.resources val packageName = context.packageName val resourceId = resources.getIdentifier(url, "raw", packageName) - val bufferedSource = Okio.buffer(Okio.source(context.resources.openRawResource(resourceId))) + val bufferedSource = context.resources.openRawResource(resourceId).source().buffer() val file = uri.toFile() file.delete() - val sink = Okio.buffer(Okio.sink(file)) + val sink = file.sink().buffer() sink.writeAll(bufferedSource) sink.close() val result = Bundle() @@ -934,13 +931,13 @@ open class FileSystemModule( override fun onResponse(call: Call, response: Response) { val file = uri.toFile() file.delete() - val sink = Okio.buffer(Okio.sink(file)) - sink.writeAll(response.body()!!.source()) + val sink = file.sink().buffer() + sink.writeAll(response.body!!.source()) sink.close() val result = Bundle().apply { putString("uri", Uri.fromFile(file).toString()) - putInt("status", response.code()) - putBundle("headers", translateHeaders(response.headers())) + putInt("status", response.code) + putBundle("headers", translateHeaders(response.headers)) if (options?.get("md5") == true) { putString("md5", md5(file)) } @@ -1003,7 +1000,7 @@ open class FileSystemModule( ?.addNetworkInterceptor { chain -> val originalResponse = chain.proceed(chain.request()) originalResponse.newBuilder() - .body(ProgressResponseBody(originalResponse.body(), progressListener)) + .body(ProgressResponseBody(originalResponse.body, progressListener)) .build() } ?.build() @@ -1098,7 +1095,7 @@ open class FileSystemModule( val options = params[0]?.options return try { val response = call!!.execute() - val responseBody = response.body() + val responseBody = response.body val input = BufferedInputStream(responseBody!!.byteStream()) val output = FileOutputStream(file, isResume == true) val data = ByteArray(1024) @@ -1108,15 +1105,15 @@ open class FileSystemModule( } val result = Bundle().apply { putString("uri", Uri.fromFile(file).toString()) - putInt("status", response.code()) - putBundle("headers", translateHeaders(response.headers())) + putInt("status", response.code) + putBundle("headers", translateHeaders(response.headers)) options?.get("md5").takeIf { it == true }?.let { putString("md5", file?.let { md5(it) }) } } response.close() promise?.resolve(result) null } catch (e: Exception) { - if (call?.isCanceled == true) { + if (call?.isCanceled() == true) { promise?.resolve(null) return null } @@ -1139,7 +1136,7 @@ open class FileSystemModule( override fun contentLength(): Long = responseBody?.contentLength() ?: -1 override fun source(): BufferedSource = - bufferedSource ?: Okio.buffer(source(responseBody!!.source())) + bufferedSource ?: source(responseBody!!.source()).buffer() private fun source(source: Source): Source { return object : ForwardingSource(source) { @@ -1304,7 +1301,7 @@ open class FileSystemModule( // Copied out of React Native's `NetworkingModule.java` private fun translateHeaders(headers: Headers): Bundle { val responseHeaders = Bundle() - for (i in 0 until headers.size()) { + for (i in 0 until headers.size) { val headerName = headers.name(i) // multiple values for the same header if (responseHeaders[headerName] != null) { diff --git a/apps/bare-expo/android/gradle.properties b/apps/bare-expo/android/gradle.properties index af980756d8cc8..28c0c1bff8eab 100644 --- a/apps/bare-expo/android/gradle.properties +++ b/apps/bare-expo/android/gradle.properties @@ -9,7 +9,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m +# Default value: -Xmx1024m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. diff --git a/apps/bare-expo/ios/Podfile.lock b/apps/bare-expo/ios/Podfile.lock index 7fe20ea09533e..2ba97e62c6140 100644 --- a/apps/bare-expo/ios/Podfile.lock +++ b/apps/bare-expo/ios/Podfile.lock @@ -122,6 +122,7 @@ PODS: - expo-dev-launcher/Main (= 0.10.1) - expo-dev-menu - expo-dev-menu-interface + - ExpoModulesCore - EXUpdatesInterface - React-Core - expo-dev-launcher/Main (0.10.1): @@ -129,12 +130,14 @@ PODS: - expo-dev-launcher/Unsafe - expo-dev-menu - expo-dev-menu-interface + - ExpoModulesCore - EXUpdatesInterface - React-Core - expo-dev-launcher/Tests (0.10.1): - EXManifests - expo-dev-menu - expo-dev-menu-interface + - ExpoModulesCore - EXUpdatesInterface - hermes-engine - Nimble @@ -147,6 +150,7 @@ PODS: - EXManifests - expo-dev-menu - expo-dev-menu-interface + - ExpoModulesCore - EXUpdatesInterface - React-Core - expo-dev-menu (0.9.1): @@ -1322,7 +1326,7 @@ SPEC CHECKSUMS: EXPermissions: f4c65fa770489cbf16ea17c3013a670671525014 Expo: 63e338fe5457b9972d10b0ece011bb76fb596744 expo-dev-client: 5fa2ce8b3e186d1734bc746e505e9e66c39cac1f - expo-dev-launcher: 859ff87239fde76a2014c0a1d21d7d8bcbc6c840 + expo-dev-launcher: 293c5980a4c5591208fd9f91ff1e9bddd0ef83b0 expo-dev-menu: d13fce788ed2cd1d0ca6aabed72cb73caa74c37b expo-dev-menu-interface: 5f95926b52150b1e5fe7d6815675c7a61245de35 ExpoCellular: f800f765c3dd91205a2b84cd4e3ea132c397cffe diff --git a/ios/ExpoKit.podspec b/ios/ExpoKit.podspec index 1e27e7e7854ef..de5dba0e40d53 100644 --- a/ios/ExpoKit.podspec +++ b/ios/ExpoKit.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.source = { :git => "http://github.com/expo/expo.git" } s.xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++14', - 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", + 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", 'OTHER_CPLUSPLUSFLAGS' => [ "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", diff --git a/ios/Exponent.xcodeproj/project.pbxproj b/ios/Exponent.xcodeproj/project.pbxproj index 245df7cc6cf65..e9375f7114e49 100644 --- a/ios/Exponent.xcodeproj/project.pbxproj +++ b/ios/Exponent.xcodeproj/project.pbxproj @@ -3814,7 +3814,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Exponent-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\""; + SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\""; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -3875,7 +3875,7 @@ STRIP_STYLE = "non-global"; SWIFT_OBJC_BRIDGING_HEADER = "Exponent-Bridging-Header.h"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\""; + SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\""; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -4028,7 +4028,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Exponent-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\""; + SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\""; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -4090,7 +4090,7 @@ STRIP_STYLE = "non-global"; SWIFT_OBJC_BRIDGING_HEADER = "Exponent-Bridging-Header.h"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\""; + SYSTEM_HEADER_SEARCH_PATHS = "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\""; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/ios/Exponent/Versioned/Core/EXVersionManager.mm b/ios/Exponent/Versioned/Core/EXVersionManager.mm index 60d7b7c3d19dc..5f9701eee63da 100644 --- a/ios/Exponent/Versioned/Core/EXVersionManager.mm +++ b/ios/Exponent/Versioned/Core/EXVersionManager.mm @@ -137,7 +137,7 @@ - (void)bridgeWillStartLoading:(id)bridge #if DEBUG || RCT_DEV if ([self _isDevModeEnabledForBridge:bridge]) { // Set the bundle url for the packager connection manually - [[RCTPackagerConnection sharedPackagerConnection] setBundleURL:[bridge bundleURL]]; + [[RCTPackagerConnection sharedPackagerConnection] reconnect:[bridge bundleURL].absoluteString]; } #endif @@ -470,13 +470,13 @@ - (Class)getModuleClassFromName:(const char *)name { // Standard if (moduleClass == RCTImageLoader.class) { - return [[moduleClass alloc] initWithRedirectDelegate:nil loadersProvider:^NSArray> *{ + return [[moduleClass alloc] initWithRedirectDelegate:nil loadersProvider:^NSArray> *(RCTModuleRegistry *) { return @[[RCTLocalAssetImageLoader new], [EXMediaLibraryImageLoader new]]; - } decodersProvider:^NSArray> *{ + } decodersProvider:^NSArray> *(RCTModuleRegistry *) { return @[[RCTGIFImageDecoder new]]; }]; } else if (moduleClass == RCTNetworking.class) { - return [[moduleClass alloc] initWithHandlersProvider:^NSArray> *{ + return [[moduleClass alloc] initWithHandlersProvider:^NSArray> *(RCTModuleRegistry *) { return @[ [RCTHTTPRequestHandler new], [RCTDataRequestHandler new], @@ -533,7 +533,12 @@ - (void *)versionedJsExecutorFactoryForBridge:(RCTBridge *)bridge [bridge moduleForClass:[RCTEventDispatcher class]]; RCTEventDispatcher *eventDispatcher = [REAEventDispatcher new]; - [eventDispatcher setBridge:bridge]; + RCTCallableJSModules *callableJSModules = [RCTCallableJSModules new]; + [bridge setValue:callableJSModules forKey:@"_callableJSModules"]; + [callableJSModules setBridge:bridge]; + [eventDispatcher setValue:callableJSModules forKey:@"_callableJSModules"]; + [eventDispatcher setValue:bridge forKey:@"_bridge"]; + [eventDispatcher initialize]; [bridge updateModuleWithInstance:eventDispatcher]; EX_WEAKIFY(self); diff --git a/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.h b/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.h index 232743c1faec2..a1880957d1ced 100644 --- a/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.h +++ b/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.h @@ -1,8 +1,9 @@ // Copyright 2015-present 650 Industries. All rights reserved. #import +#import #import -@interface EXDisabledDevLoadingView : RCTEventEmitter +@interface EXDisabledDevLoadingView : RCTEventEmitter @end diff --git a/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.m b/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.m index 8498c644ffbde..1cc17f82cf95d 100644 --- a/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.m +++ b/ios/Exponent/Versioned/Core/Internal/DevSupport/EXDisabledDevLoadingView.m @@ -45,5 +45,22 @@ - (void)stopObserving _isObserving = NO; } +// RCTDevLoadingViewProtocol implementations + ++ (void)setEnabled:(BOOL)enabled +{ + +} + +- (void)showWithURL:(NSURL *)URL +{ + +} + +- (void)updateProgress:(RCTLoadingProgress *)progress +{ + +} + @end diff --git a/ios/Exponent/Versioned/Core/UniversalModules/EXScopedBranch.m b/ios/Exponent/Versioned/Core/UniversalModules/EXScopedBranch.m index 497b926f4c8b0..1d5d88f55ccf0 100644 --- a/ios/Exponent/Versioned/Core/UniversalModules/EXScopedBranch.m +++ b/ios/Exponent/Versioned/Core/UniversalModules/EXScopedBranch.m @@ -4,7 +4,7 @@ @interface EXScopedBranch () -@property (nonatomic, weak) EXModuleRegistry *moduleRegistry; +@property (nonatomic, weak) EXModuleRegistry *exModuleRegistry; @end @@ -29,10 +29,10 @@ - (instancetype)initWithScopeKey:(NSString *)scopeKey return self; } -- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry +- (void)setModuleRegistry:(EXModuleRegistry *)exModuleRegistry { - _moduleRegistry = moduleRegistry; - [(id)[_moduleRegistry getSingletonModuleForName:@"BranchManager"] branchModuleDidInit:self]; + _exModuleRegistry = exModuleRegistry; + [(id)[_exModuleRegistry getSingletonModuleForName:@"BranchManager"] branchModuleDidInit:self]; } - (void)setBridge:(RCTBridge *)bridge diff --git a/ios/Podfile b/ios/Podfile index 4193eb1f5be57..434691f56ed6b 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -52,6 +52,9 @@ abstract_target 'Expo Go' do # Build React Native with RCT_DEV enabled and RCT_ENABLE_INSPECTOR and # RCT_ENABLE_PACKAGER_CONNECTION disabled post_install do |installer| + # Workaround build error for Folly + __apply_Xcode_12_5_M1_post_install_workaround(installer) if installer.pods_project + # Disabled as of CocoaPods 1.8.0.beta1 since pods_project seems to be nil # installer.pods_project.main_group.tab_width = '2'; # installer.pods_project.main_group.indent_width = '2'; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e2f41cbc216d6..581adc368f14d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -296,7 +296,7 @@ PODS: - ABI42_0_0React-Core (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0lottie-react-native (4.0.2): - ABI42_0_0React-Core - lottie-ios (~> 3.2.3) @@ -305,7 +305,7 @@ PODS: - ABI42_0_0FBLazyVector (= 0.63.2) - ABI42_0_0RCTRequired (= 0.63.2) - ABI42_0_0React-Core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React (0.63.2): - ABI42_0_0React-Core (= 0.63.2) - ABI42_0_0React-Core/DevSupport (= 0.63.2) @@ -327,7 +327,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/CoreModulesHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -335,14 +335,14 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/Default (0.63.2): - ABI42_0_0React-cxxreact (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/DevSupport (0.63.2): - ABI42_0_0React-Core/Default (= 0.63.2) - ABI42_0_0React-Core/RCTWebSocket (= 0.63.2) @@ -352,7 +352,7 @@ PODS: - ABI42_0_0React-jsinspector (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTActionSheetHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -360,7 +360,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTAnimationHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -368,7 +368,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTBlobHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -376,7 +376,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTImageHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -384,7 +384,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTLinkingHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -392,7 +392,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTNetworkHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -400,7 +400,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTSettingsHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -408,7 +408,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTTextHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -416,7 +416,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTVibrationHeaders (0.63.2): - ABI42_0_0React-Core/Default - ABI42_0_0React-cxxreact (= 0.63.2) @@ -424,7 +424,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-Core/RCTWebSocket (0.63.2): - ABI42_0_0React-Core/Default (= 0.63.2) - ABI42_0_0React-cxxreact (= 0.63.2) @@ -432,7 +432,7 @@ PODS: - ABI42_0_0React-jsiexecutor (= 0.63.2) - ABI42_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-CoreModules (0.63.2): - ABI42_0_0FBReactNativeSpec (= 0.63.2) - ABI42_0_0RCTTypeSafety (= 0.63.2) @@ -440,31 +440,31 @@ PODS: - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0React-RCTImage (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-cxxreact (0.63.2): - ABI42_0_0React-callinvoker (= 0.63.2) - ABI42_0_0React-jsinspector (= 0.63.2) - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-jsi (0.63.2): - ABI42_0_0React-jsi/Default (= 0.63.2) - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-jsi/Default (0.63.2): - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-jsiexecutor (0.63.2): - ABI42_0_0React-cxxreact (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-jsinspector (0.63.2) - ABI42_0_0react-native-webview (11.6.2): - ABI42_0_0React-Core @@ -476,7 +476,7 @@ PODS: - ABI42_0_0React-Core/RCTAnimationHeaders (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-RCTBlob (0.63.2): - ABI42_0_0FBReactNativeSpec (= 0.63.2) - ABI42_0_0React-Core/RCTBlobHeaders (= 0.63.2) @@ -484,7 +484,7 @@ PODS: - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0React-RCTNetwork (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-RCTImage (0.63.2): - ABI42_0_0FBReactNativeSpec (= 0.63.2) - ABI42_0_0RCTTypeSafety (= 0.63.2) @@ -492,7 +492,7 @@ PODS: - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0React-RCTNetwork (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-RCTLinking (0.63.2): - ABI42_0_0FBReactNativeSpec (= 0.63.2) - ABI42_0_0React-Core/RCTLinkingHeaders (= 0.63.2) @@ -504,14 +504,14 @@ PODS: - ABI42_0_0React-Core/RCTNetworkHeaders (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-RCTSettings (0.63.2): - ABI42_0_0FBReactNativeSpec (= 0.63.2) - ABI42_0_0RCTTypeSafety (= 0.63.2) - ABI42_0_0React-Core/RCTSettingsHeaders (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0React-RCTText (0.63.2): - ABI42_0_0React-Core/RCTTextHeaders (= 0.63.2) - ABI42_0_0React-RCTVibration (0.63.2): @@ -519,7 +519,7 @@ PODS: - ABI42_0_0React-Core/RCTVibrationHeaders (= 0.63.2) - ABI42_0_0React-jsi (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0ReactCommon (0.63.2): - ABI42_0_0ReactCommon/turbomodule (= 0.63.2) - ABI42_0_0ReactCommon/turbomodule (0.63.2): @@ -531,7 +531,7 @@ PODS: - ABI42_0_0ReactCommon/turbomodule/samples (= 0.63.2) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0ReactCommon/turbomodule/core (0.63.2): - ABI42_0_0React-callinvoker (= 0.63.2) - ABI42_0_0React-Core (= 0.63.2) @@ -539,7 +539,7 @@ PODS: - ABI42_0_0React-jsi (= 0.63.2) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0ReactCommon/turbomodule/samples (0.63.2): - ABI42_0_0React-callinvoker (= 0.63.2) - ABI42_0_0React-Core (= 0.63.2) @@ -548,7 +548,7 @@ PODS: - ABI42_0_0ReactCommon/turbomodule/core (= 0.63.2) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI42_0_0RNReanimated (2.2.0): - ABI42_0_0RCTRequired - ABI42_0_0RCTTypeSafety @@ -863,7 +863,7 @@ PODS: - ABI43_0_0React-Core (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0lottie-react-native (4.0.3): - ABI43_0_0React-Core - lottie-ios (~> 3.2.3) @@ -872,7 +872,7 @@ PODS: - ABI43_0_0FBLazyVector (= 0.64.3) - ABI43_0_0RCTRequired (= 0.64.3) - ABI43_0_0React-Core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React (0.64.3): - ABI43_0_0React-Core (= 0.64.3) - ABI43_0_0React-Core/DevSupport (= 0.64.3) @@ -895,7 +895,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/CoreModulesHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -904,7 +904,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/Default (0.64.3): - ABI43_0_0React-cxxreact (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) @@ -912,7 +912,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/DevSupport (0.64.3): - ABI43_0_0React-Core/Default (= 0.64.3) - ABI43_0_0React-Core/RCTWebSocket (= 0.64.3) @@ -923,7 +923,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTActionSheetHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -932,7 +932,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTAnimationHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -941,7 +941,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTBlobHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -950,7 +950,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTImageHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -959,7 +959,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTLinkingHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -968,7 +968,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTNetworkHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -977,7 +977,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTSettingsHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -986,7 +986,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTTextHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -995,7 +995,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTVibrationHeaders (0.64.3): - ABI43_0_0React-Core/Default - ABI43_0_0React-cxxreact (= 0.64.3) @@ -1004,7 +1004,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-Core/RCTWebSocket (0.64.3): - ABI43_0_0React-Core/Default (= 0.64.3) - ABI43_0_0React-cxxreact (= 0.64.3) @@ -1013,7 +1013,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-CoreModules (0.64.3): - ABI43_0_0FBReactNativeSpec (= 0.64.3) - ABI43_0_0RCTTypeSafety (= 0.64.3) @@ -1021,35 +1021,35 @@ PODS: - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0React-RCTImage (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-cxxreact (0.64.3): - ABI43_0_0React-callinvoker (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0React-jsinspector (= 0.64.3) - ABI43_0_0React-perflogger (= 0.64.3) - ABI43_0_0React-runtimeexecutor (= 0.64.3) - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-jsi (0.64.3): - ABI43_0_0React-jsi/Default (= 0.64.3) - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-jsi/Default (0.64.3): - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-jsiexecutor (0.64.3): - ABI43_0_0React-cxxreact (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0React-perflogger (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-jsinspector (0.64.3) - ABI43_0_0react-native-netinfo (6.0.2): - ABI43_0_0React-Core @@ -1068,7 +1068,7 @@ PODS: - ABI43_0_0React-Core/RCTAnimationHeaders (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-RCTBlob (0.64.3): - ABI43_0_0FBReactNativeSpec (= 0.64.3) - ABI43_0_0React-Core/RCTBlobHeaders (= 0.64.3) @@ -1076,7 +1076,7 @@ PODS: - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0React-RCTNetwork (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-RCTImage (0.64.3): - ABI43_0_0FBReactNativeSpec (= 0.64.3) - ABI43_0_0RCTTypeSafety (= 0.64.3) @@ -1084,7 +1084,7 @@ PODS: - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0React-RCTNetwork (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-RCTLinking (0.64.3): - ABI43_0_0FBReactNativeSpec (= 0.64.3) - ABI43_0_0React-Core/RCTLinkingHeaders (= 0.64.3) @@ -1096,14 +1096,14 @@ PODS: - ABI43_0_0React-Core/RCTNetworkHeaders (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-RCTSettings (0.64.3): - ABI43_0_0FBReactNativeSpec (= 0.64.3) - ABI43_0_0RCTTypeSafety (= 0.64.3) - ABI43_0_0React-Core/RCTSettingsHeaders (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-RCTText (0.64.3): - ABI43_0_0React-Core/RCTTextHeaders (= 0.64.3) - ABI43_0_0React-RCTVibration (0.64.3): @@ -1111,7 +1111,7 @@ PODS: - ABI43_0_0React-Core/RCTVibrationHeaders (= 0.64.3) - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0React-runtimeexecutor (0.64.3): - ABI43_0_0React-jsi (= 0.64.3) - ABI43_0_0ReactCommon (0.64.3): @@ -1126,7 +1126,7 @@ PODS: - ABI43_0_0ReactCommon/turbomodule/samples (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0ReactCommon/turbomodule/core (0.64.3): - ABI43_0_0React-callinvoker (= 0.64.3) - ABI43_0_0React-Core (= 0.64.3) @@ -1135,7 +1135,7 @@ PODS: - ABI43_0_0React-perflogger (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0ReactCommon/turbomodule/samples (0.64.3): - ABI43_0_0React-callinvoker (= 0.64.3) - ABI43_0_0React-Core (= 0.64.3) @@ -1145,7 +1145,7 @@ PODS: - ABI43_0_0ReactCommon/turbomodule/core (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI43_0_0RNReanimated (2.2.0): - ABI43_0_0RCTRequired - ABI43_0_0RCTTypeSafety @@ -1465,7 +1465,7 @@ PODS: - ABI44_0_0React-Core (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0lottie-react-native (5.0.1): - ABI44_0_0React-Core - lottie-ios (~> 3.2.3) @@ -1474,7 +1474,7 @@ PODS: - ABI44_0_0FBLazyVector (= 0.64.3) - ABI44_0_0RCTRequired (= 0.64.3) - ABI44_0_0React-Core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React (0.64.3): - ABI44_0_0React-Core (= 0.64.3) - ABI44_0_0React-Core/DevSupport (= 0.64.3) @@ -1497,7 +1497,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/CoreModulesHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1506,7 +1506,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/Default (0.64.3): - ABI44_0_0React-cxxreact (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) @@ -1514,7 +1514,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/DevSupport (0.64.3): - ABI44_0_0React-Core/Default (= 0.64.3) - ABI44_0_0React-Core/RCTWebSocket (= 0.64.3) @@ -1525,7 +1525,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTActionSheetHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1534,7 +1534,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTAnimationHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1543,7 +1543,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTBlobHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1552,7 +1552,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTImageHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1561,7 +1561,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTLinkingHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1570,7 +1570,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTNetworkHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1579,7 +1579,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTSettingsHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1588,7 +1588,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTTextHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1597,7 +1597,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTVibrationHeaders (0.64.3): - ABI44_0_0React-Core/Default - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1606,7 +1606,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-Core/RCTWebSocket (0.64.3): - ABI44_0_0React-Core/Default (= 0.64.3) - ABI44_0_0React-cxxreact (= 0.64.3) @@ -1615,7 +1615,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0Yoga - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-CoreModules (0.64.3): - ABI44_0_0FBReactNativeSpec (= 0.64.3) - ABI44_0_0RCTTypeSafety (= 0.64.3) @@ -1623,35 +1623,35 @@ PODS: - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0React-RCTImage (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-cxxreact (0.64.3): - ABI44_0_0React-callinvoker (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0React-jsinspector (= 0.64.3) - ABI44_0_0React-perflogger (= 0.64.3) - ABI44_0_0React-runtimeexecutor (= 0.64.3) - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-jsi (0.64.3): - ABI44_0_0React-jsi/Default (= 0.64.3) - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-jsi/Default (0.64.3): - - boost-for-react-native (= 1.63.0) + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-jsiexecutor (0.64.3): - ABI44_0_0React-cxxreact (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0React-perflogger (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-jsinspector (0.64.3) - ABI44_0_0react-native-netinfo (7.1.3): - ABI44_0_0React-Core @@ -1670,7 +1670,7 @@ PODS: - ABI44_0_0React-Core/RCTAnimationHeaders (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-RCTBlob (0.64.3): - ABI44_0_0FBReactNativeSpec (= 0.64.3) - ABI44_0_0React-Core/RCTBlobHeaders (= 0.64.3) @@ -1678,7 +1678,7 @@ PODS: - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0React-RCTNetwork (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-RCTImage (0.64.3): - ABI44_0_0FBReactNativeSpec (= 0.64.3) - ABI44_0_0RCTTypeSafety (= 0.64.3) @@ -1686,7 +1686,7 @@ PODS: - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0React-RCTNetwork (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-RCTLinking (0.64.3): - ABI44_0_0FBReactNativeSpec (= 0.64.3) - ABI44_0_0React-Core/RCTLinkingHeaders (= 0.64.3) @@ -1698,14 +1698,14 @@ PODS: - ABI44_0_0React-Core/RCTNetworkHeaders (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-RCTSettings (0.64.3): - ABI44_0_0FBReactNativeSpec (= 0.64.3) - ABI44_0_0RCTTypeSafety (= 0.64.3) - ABI44_0_0React-Core/RCTSettingsHeaders (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-RCTText (0.64.3): - ABI44_0_0React-Core/RCTTextHeaders (= 0.64.3) - ABI44_0_0React-RCTVibration (0.64.3): @@ -1713,7 +1713,7 @@ PODS: - ABI44_0_0React-Core/RCTVibrationHeaders (= 0.64.3) - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0React-runtimeexecutor (0.64.3): - ABI44_0_0React-jsi (= 0.64.3) - ABI44_0_0ReactCommon (0.64.3): @@ -1728,7 +1728,7 @@ PODS: - ABI44_0_0ReactCommon/turbomodule/samples (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0ReactCommon/turbomodule/core (0.64.3): - ABI44_0_0React-callinvoker (= 0.64.3) - ABI44_0_0React-Core (= 0.64.3) @@ -1737,7 +1737,7 @@ PODS: - ABI44_0_0React-perflogger (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0ReactCommon/turbomodule/samples (0.64.3): - ABI44_0_0React-callinvoker (= 0.64.3) - ABI44_0_0React-Core (= 0.64.3) @@ -1747,7 +1747,7 @@ PODS: - ABI44_0_0ReactCommon/turbomodule/core (= 0.64.3) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - ABI44_0_0RNGestureHandler (2.1.0): - ABI44_0_0React-Core - ABI44_0_0RNReanimated (2.3.1): @@ -1796,7 +1796,7 @@ PODS: - AppAuth/Core (1.4.0) - AppAuth/ExternalUserAgent (1.4.0) - ASN1Decoder (1.8.0) - - boost-for-react-native (1.63.0) + - boost (1.76.0) - Branch (0.35.0) - CocoaLumberjack (3.5.3): - CocoaLumberjack/Core (= 3.5.3) @@ -2001,14 +2001,14 @@ PODS: - FacebookSDK/CoreKit - FBAudienceNetwork (6.5.0): - FBSDKCoreKit/Basics (>= 7.0.1) - - FBLazyVector (0.64.3) - - FBReactNativeSpec (0.64.3): - - RCT-Folly (= 2020.01.13.00) - - RCTRequired (= 0.64.3) - - RCTTypeSafety (= 0.64.3) - - React-Core (= 0.64.3) - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) + - FBLazyVector (0.67.2) + - FBReactNativeSpec (0.67.2): + - RCT-Folly (= 2021.06.28.00-v2) + - RCTRequired (= 0.67.2) + - RCTTypeSafety (= 0.67.2) + - React-Core (= 0.67.2) + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) - FBSDKCoreKit (9.2.0): - FBSDKCoreKit/Basics (= 9.2.0) - FBSDKCoreKit/Core (= 9.2.0) @@ -2043,6 +2043,7 @@ PODS: - GoogleUtilities/Environment (~> 7.0) - GoogleUtilities/UserDefaults (~> 7.0) - PromisesObjC (~> 1.2) + - fmt (6.2.1) - glog (0.3.5) - Google-Maps-iOS-Utils (2.1.0): - Google-Maps-iOS-Utils/Clustering (= 2.1.0) @@ -2073,10 +2074,10 @@ PODS: - GoogleUtilities/Environment (~> 7.2) - nanopb (~> 2.30907.0) - PromisesObjC (~> 1.2) - - GoogleMaps (3.6.0): - - GoogleMaps/Maps (= 3.6.0) - - GoogleMaps/Base (3.6.0) - - GoogleMaps/Maps (3.6.0): + - GoogleMaps (3.10.0): + - GoogleMaps/Maps (= 3.10.0) + - GoogleMaps/Base (3.10.0) + - GoogleMaps/Maps (3.10.0): - GoogleMaps/Base - GoogleMLKit/FaceDetection (2.1.0): - GoogleMLKit/MLKitCore @@ -2100,24 +2101,24 @@ PODS: - "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)" - "GoogleToolboxForMac/NSString+URLArguments (2.3.2)" - GoogleUserMessagingPlatform (1.4.0) - - GoogleUtilities/AppDelegateSwizzler (7.6.0): + - GoogleUtilities/AppDelegateSwizzler (7.7.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.6.0): + - GoogleUtilities/Environment (7.7.0): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.6.0): + - GoogleUtilities/Logger (7.7.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.6.0): + - GoogleUtilities/MethodSwizzler (7.7.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (7.6.0): + - GoogleUtilities/Network (7.7.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.6.0)" - - GoogleUtilities/Reachability (7.6.0): + - "GoogleUtilities/NSData+zlib (7.7.0)" + - GoogleUtilities/Reachability (7.7.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.6.0): + - GoogleUtilities/UserDefaults (7.7.0): - GoogleUtilities/Logger - GoogleUtilitiesComponents (1.1.0): - GoogleUtilities/Logger @@ -2161,199 +2162,204 @@ PODS: - PromisesObjC (1.2.12) - Protobuf (3.19.1) - Quick (4.0.0) - - RCT-Folly (2020.01.13.00): - - boost-for-react-native + - RCT-Folly (2021.06.28.00-v2): + - boost - DoubleConversion + - fmt (~> 6.2.1) - glog - - RCT-Folly/Default (= 2020.01.13.00) - - RCT-Folly/Default (2020.01.13.00): - - boost-for-react-native + - RCT-Folly/Default (= 2021.06.28.00-v2) + - RCT-Folly/Default (2021.06.28.00-v2): + - boost - DoubleConversion + - fmt (~> 6.2.1) - glog - - RCTRequired (0.64.3) - - RCTTypeSafety (0.64.3): - - FBLazyVector (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - RCTRequired (= 0.64.3) - - React-Core (= 0.64.3) - - React (0.64.3): - - React-Core (= 0.64.3) - - React-Core/DevSupport (= 0.64.3) - - React-Core/RCTWebSocket (= 0.64.3) - - React-RCTActionSheet (= 0.64.3) - - React-RCTAnimation (= 0.64.3) - - React-RCTBlob (= 0.64.3) - - React-RCTImage (= 0.64.3) - - React-RCTLinking (= 0.64.3) - - React-RCTNetwork (= 0.64.3) - - React-RCTSettings (= 0.64.3) - - React-RCTText (= 0.64.3) - - React-RCTVibration (= 0.64.3) - - React-callinvoker (0.64.3) - - React-Core (0.64.3): + - RCTRequired (0.67.2) + - RCTTypeSafety (0.67.2): + - FBLazyVector (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTRequired (= 0.67.2) + - React-Core (= 0.67.2) + - React (0.67.2): + - React-Core (= 0.67.2) + - React-Core/DevSupport (= 0.67.2) + - React-Core/RCTWebSocket (= 0.67.2) + - React-RCTActionSheet (= 0.67.2) + - React-RCTAnimation (= 0.67.2) + - React-RCTBlob (= 0.67.2) + - React-RCTImage (= 0.67.2) + - React-RCTLinking (= 0.67.2) + - React-RCTNetwork (= 0.67.2) + - React-RCTSettings (= 0.67.2) + - React-RCTText (= 0.67.2) + - React-RCTVibration (= 0.67.2) + - React-callinvoker (0.67.2) + - React-Core (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) - - React-Core/Default (= 0.64.3) - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - RCT-Folly (= 2021.06.28.00-v2) + - React-Core/Default (= 0.67.2) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/CoreModulesHeaders (0.64.3): + - React-Core/CoreModulesHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/Default (0.64.3): + - React-Core/Default (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - RCT-Folly (= 2021.06.28.00-v2) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/DevSupport (0.64.3): + - React-Core/DevSupport (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) - - React-Core/Default (= 0.64.3) - - React-Core/RCTWebSocket (= 0.64.3) - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-jsinspector (= 0.64.3) - - React-perflogger (= 0.64.3) + - RCT-Folly (= 2021.06.28.00-v2) + - React-Core/Default (= 0.67.2) + - React-Core/RCTWebSocket (= 0.67.2) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-jsinspector (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTActionSheetHeaders (0.64.3): + - React-Core/RCTActionSheetHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTAnimationHeaders (0.64.3): + - React-Core/RCTAnimationHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTBlobHeaders (0.64.3): + - React-Core/RCTBlobHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTImageHeaders (0.64.3): + - React-Core/RCTImageHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTLinkingHeaders (0.64.3): + - React-Core/RCTLinkingHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTNetworkHeaders (0.64.3): + - React-Core/RCTNetworkHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTSettingsHeaders (0.64.3): + - React-Core/RCTSettingsHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTTextHeaders (0.64.3): + - React-Core/RCTTextHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTVibrationHeaders (0.64.3): + - React-Core/RCTVibrationHeaders (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) + - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-Core/RCTWebSocket (0.64.3): + - React-Core/RCTWebSocket (0.67.2): - glog - - RCT-Folly (= 2020.01.13.00) - - React-Core/Default (= 0.64.3) - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsiexecutor (= 0.64.3) - - React-perflogger (= 0.64.3) + - RCT-Folly (= 2021.06.28.00-v2) + - React-Core/Default (= 0.67.2) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsiexecutor (= 0.67.2) + - React-perflogger (= 0.67.2) - Yoga - - React-CoreModules (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - RCTTypeSafety (= 0.64.3) - - React-Core/CoreModulesHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - React-RCTImage (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-cxxreact (0.64.3): - - boost-for-react-native (= 1.63.0) + - React-CoreModules (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTTypeSafety (= 0.67.2) + - React-Core/CoreModulesHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - React-RCTImage (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-cxxreact (0.67.2): + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) - - React-callinvoker (= 0.64.3) - - React-jsi (= 0.64.3) - - React-jsinspector (= 0.64.3) - - React-perflogger (= 0.64.3) - - React-runtimeexecutor (= 0.64.3) - - React-jsi (0.64.3): - - boost-for-react-native (= 1.63.0) + - RCT-Folly (= 2021.06.28.00-v2) + - React-callinvoker (= 0.67.2) + - React-jsi (= 0.67.2) + - React-jsinspector (= 0.67.2) + - React-logger (= 0.67.2) + - React-perflogger (= 0.67.2) + - React-runtimeexecutor (= 0.67.2) + - React-jsi (0.67.2): + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) - - React-jsi/Default (= 0.64.3) - - React-jsi/Default (0.64.3): - - boost-for-react-native (= 1.63.0) + - RCT-Folly (= 2021.06.28.00-v2) + - React-jsi/Default (= 0.67.2) + - React-jsi/Default (0.67.2): + - boost (= 1.76.0) - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) - - React-jsiexecutor (0.64.3): + - RCT-Folly (= 2021.06.28.00-v2) + - React-jsiexecutor (0.67.2): - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-perflogger (= 0.64.3) - - React-jsinspector (0.64.3) + - RCT-Folly (= 2021.06.28.00-v2) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-perflogger (= 0.67.2) + - React-jsinspector (0.67.2) + - React-logger (0.67.2): + - glog - react-native-netinfo (7.1.3): - React-Core - react-native-pager-view (5.4.9): @@ -2362,70 +2368,71 @@ PODS: - React-Core - react-native-webview (11.15.0): - React-Core - - React-perflogger (0.64.3) - - React-RCTActionSheet (0.64.3): - - React-Core/RCTActionSheetHeaders (= 0.64.3) - - React-RCTAnimation (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - RCTTypeSafety (= 0.64.3) - - React-Core/RCTAnimationHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-RCTBlob (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - React-Core/RCTBlobHeaders (= 0.64.3) - - React-Core/RCTWebSocket (= 0.64.3) - - React-jsi (= 0.64.3) - - React-RCTNetwork (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-RCTImage (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - RCTTypeSafety (= 0.64.3) - - React-Core/RCTImageHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - React-RCTNetwork (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-RCTLinking (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - React-Core/RCTLinkingHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-RCTNetwork (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - RCTTypeSafety (= 0.64.3) - - React-Core/RCTNetworkHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-RCTSettings (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - RCTTypeSafety (= 0.64.3) - - React-Core/RCTSettingsHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-RCTText (0.64.3): - - React-Core/RCTTextHeaders (= 0.64.3) - - React-RCTVibration (0.64.3): - - FBReactNativeSpec (= 0.64.3) - - RCT-Folly (= 2020.01.13.00) - - React-Core/RCTVibrationHeaders (= 0.64.3) - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (= 0.64.3) - - React-runtimeexecutor (0.64.3): - - React-jsi (= 0.64.3) - - ReactCommon/turbomodule/core (0.64.3): + - React-perflogger (0.67.2) + - React-RCTActionSheet (0.67.2): + - React-Core/RCTActionSheetHeaders (= 0.67.2) + - React-RCTAnimation (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTTypeSafety (= 0.67.2) + - React-Core/RCTAnimationHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-RCTBlob (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - React-Core/RCTBlobHeaders (= 0.67.2) + - React-Core/RCTWebSocket (= 0.67.2) + - React-jsi (= 0.67.2) + - React-RCTNetwork (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-RCTImage (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTTypeSafety (= 0.67.2) + - React-Core/RCTImageHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - React-RCTNetwork (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-RCTLinking (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - React-Core/RCTLinkingHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-RCTNetwork (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTTypeSafety (= 0.67.2) + - React-Core/RCTNetworkHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-RCTSettings (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTTypeSafety (= 0.67.2) + - React-Core/RCTSettingsHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-RCTText (0.67.2): + - React-Core/RCTTextHeaders (= 0.67.2) + - React-RCTVibration (0.67.2): + - FBReactNativeSpec (= 0.67.2) + - RCT-Folly (= 2021.06.28.00-v2) + - React-Core/RCTVibrationHeaders (= 0.67.2) + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (= 0.67.2) + - React-runtimeexecutor (0.67.2): + - React-jsi (= 0.67.2) + - ReactCommon/turbomodule/core (0.67.2): - DoubleConversion - glog - - RCT-Folly (= 2020.01.13.00) - - React-callinvoker (= 0.64.3) - - React-Core (= 0.64.3) - - React-cxxreact (= 0.64.3) - - React-jsi (= 0.64.3) - - React-perflogger (= 0.64.3) + - RCT-Folly (= 2021.06.28.00-v2) + - React-callinvoker (= 0.67.2) + - React-Core (= 0.67.2) + - React-cxxreact (= 0.67.2) + - React-jsi (= 0.67.2) + - React-logger (= 0.67.2) + - React-perflogger (= 0.67.2) - RNGestureHandler (2.1.0): - React-Core - RNReanimated (2.3.1): @@ -2807,6 +2814,7 @@ DEPENDENCIES: - ABI44_0_0UMTaskManagerInterface (from `./versioned/sdk44/UMTaskManagerInterface`) - ABI44_0_0Yoga (from `./versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/yoga`) - Amplitude + - boost (from `../react-native-lab/react-native/third-party-podspecs/boost.podspec`) - CocoaLumberjack (~> 3.5.3) - DoubleConversion (from `../react-native-lab/react-native/third-party-podspecs/DoubleConversion.podspec`) - EXAdsAdMob (from `../packages/expo-ads-admob/ios`) @@ -2908,6 +2916,7 @@ DEPENDENCIES: - React-jsi (from `../react-native-lab/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../react-native-lab/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../react-native-lab/react-native/ReactCommon/jsinspector`) + - React-logger (from `../react-native-lab/react-native/ReactCommon/logger`) - "react-native-netinfo (from `./vendored/unversioned/@react-native-community/netinfo`)" - react-native-pager-view (from `./vendored/unversioned/react-native-pager-view`) - "react-native-segmented-control (from `./vendored/unversioned/@react-native-segmented-control/segmented-control`)" @@ -2938,7 +2947,6 @@ SPEC REPOS: - Analytics - AppAuth - ASN1Decoder - - boost-for-react-native - Branch - CocoaLumberjack - FacebookSDK @@ -2949,6 +2957,7 @@ SPEC REPOS: - FirebaseCore - FirebaseCoreDiagnostics - FirebaseInstallations + - fmt - Google-Maps-iOS-Utils - Google-Mobile-Ads-SDK - GoogleAppMeasurement @@ -3606,6 +3615,8 @@ EXTERNAL SOURCES: :path: "./versioned/sdk44/UMTaskManagerInterface" ABI44_0_0Yoga: :path: "./versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/yoga" + boost: + :podspec: "../react-native-lab/react-native/third-party-podspecs/boost.podspec" DoubleConversion: :podspec: "../react-native-lab/react-native/third-party-podspecs/DoubleConversion.podspec" EXAdsAdMob: @@ -3786,6 +3797,8 @@ EXTERNAL SOURCES: :path: "../react-native-lab/react-native/ReactCommon/jsiexecutor" React-jsinspector: :path: "../react-native-lab/react-native/ReactCommon/jsinspector" + React-logger: + :path: "../react-native-lab/react-native/ReactCommon/logger" react-native-netinfo: :path: "./vendored/unversioned/@react-native-community/netinfo" react-native-pager-view: @@ -3881,7 +3894,7 @@ SPEC CHECKSUMS: ABI42_0_0EXNetwork: 0176a370ca963ed0c9c7bec2d6938cff05e94a1e ABI42_0_0EXNotifications: 36428b03b301a9b51de900910313e12e150a07b0 ABI42_0_0EXPermissions: 7f0f7a1334056db7f51c3563fd977d0a6393c998 - ABI42_0_0ExpoKit: 314e91dcab92ec8a886d1fa5a9c3f8b457d24440 + ABI42_0_0ExpoKit: 3a6ca193ab76ee640f01accc98e0570477393db4 ABI42_0_0ExpoModulesCore: 85d79294f8c6c297f1b1c1af7a5ef292dd21a643 ABI42_0_0EXPrint: 104311da09fe260e5d21d83d235c0c557d826282 ABI42_0_0EXRandom: e692f2004e285512b969c9a0ce488283b6149370 @@ -3904,30 +3917,30 @@ SPEC CHECKSUMS: ABI42_0_0EXVideoThumbnails: 4c70c64a6b97aa4ff5f88382fe38f7549bc03e31 ABI42_0_0EXWebBrowser: 54d4399ed04c284e183a90d0439b96fd041d2900 ABI42_0_0FBLazyVector: b7ece1b8817a64a6e50199e5308f4f64f9c2258d - ABI42_0_0FBReactNativeSpec: db1207c0dc76a1c1fc6f380f25a53ab4f6d94e26 + ABI42_0_0FBReactNativeSpec: f0c15e8d150d89c17dc49b08b68cf0f2f81abecf ABI42_0_0lottie-react-native: 41c5bd906ed2e90b7bf87b98b99ff151ea2f0bcc ABI42_0_0RCTRequired: ef174cb948cb39f1816890f70bda9402c9e5fc49 - ABI42_0_0RCTTypeSafety: d32f0a5f43c09760e390e997525f9502d12031e6 + ABI42_0_0RCTTypeSafety: bac9fbc24a21999828693a7365d649c10a1f470f ABI42_0_0React: d1063307b9f7b60d60d1e0c55d12382711bc26ec ABI42_0_0React-callinvoker: 086c212efd9953fa8be01c44cef163400a244d46 - ABI42_0_0React-Core: ed06fd88a48e4c691767da4a9f3934ec0be22f2b - ABI42_0_0React-CoreModules: dd968829f374063db21b3786605d41d818a47b1d - ABI42_0_0React-cxxreact: 6cbc321344ff5ca934d43651efab27a6eea08691 - ABI42_0_0React-jsi: 1e6d772d36093c1c096d69a46261ad7483193e17 - ABI42_0_0React-jsiexecutor: e5466d1aff1213c610efc05d7bd3f7d0fe4ede9b + ABI42_0_0React-Core: 8b111abe63871f8f026fc4848230333cf59afcef + ABI42_0_0React-CoreModules: e6941b5f504a1ce94f0e9aade147e0da04d865ac + ABI42_0_0React-cxxreact: 44d850460b32c7be5ed1e42fd4338c0b710b0e6b + ABI42_0_0React-jsi: 8504c2dd2640952e48f8b76c7e36d7943cae5335 + ABI42_0_0React-jsiexecutor: 615b20e9cac57a495ad9fc6509e14f876698d369 ABI42_0_0React-jsinspector: 844a95fcc0a547ae45856ab5744a8a9b464c4860 ABI42_0_0react-native-webview: c38e0593687ae0393f4314d5be007469d8827b4f ABI42_0_0React-RCTActionSheet: f18c9953c0817e0632e6e1daf42b5c23d47340bf - ABI42_0_0React-RCTAnimation: ebbfc37a6dc5bf3e1371280ccb343e6e4dc23133 - ABI42_0_0React-RCTBlob: a678216f8c27b1f3cab5f686d24b758d5202501c - ABI42_0_0React-RCTImage: 3aa1f9289d04d8f447b3361ed879946135969cb4 - ABI42_0_0React-RCTLinking: c4d9d75818c6419dda2d8a5777ba0c6d4bc6c571 - ABI42_0_0React-RCTNetwork: 1370a07d51ed139b420ad3dbbecc9d81ffb1ff71 - ABI42_0_0React-RCTSettings: 8d8f6b9bbc405dcc3d337355c50c3a77c5445804 + ABI42_0_0React-RCTAnimation: 114bf1b9fb6f0f6740d75d23076ecf29a570fef7 + ABI42_0_0React-RCTBlob: 72f75f824a62d03e294391ef2f1c1fb18abb6ffd + ABI42_0_0React-RCTImage: c9b553a5f5ce76c9c4c506a2a4c566ff33a75696 + ABI42_0_0React-RCTLinking: c987b575841182a969a9656ed0f7a9ac9f90e564 + ABI42_0_0React-RCTNetwork: 1241661fc6e6374809e4081bdedd6162b1e80061 + ABI42_0_0React-RCTSettings: 2c48913f3078386c89a34cb324c8252730c6d3e8 ABI42_0_0React-RCTText: dadc12ceba79780500672bbd6e91cd2a3dcdbb35 - ABI42_0_0React-RCTVibration: e33d9640b1ce92f1e1c07989523289a7187ce9b1 - ABI42_0_0ReactCommon: c3bb4c4691c125880ac8c6d6a13a44744059738a - ABI42_0_0RNReanimated: c83b815abf46671c46634e2b3d3813e434ef174f + ABI42_0_0React-RCTVibration: ab08c9a2ca999a30dcfa8796f567f07c7a992ffe + ABI42_0_0ReactCommon: 7603d11b962071f1dfb26d95f187d8ab1d3510f7 + ABI42_0_0RNReanimated: 56259ea2d2d56e54b7fde913223efc969611f94f ABI42_0_0RNScreens: 54491fe4857af5649b9bbcdb68f80bfaea9184df ABI42_0_0stripe-react-native: e3d0209803fd43b26336460f738c7db88bb7b850 ABI42_0_0UMAppLoader: dabb1f8c2f864f5559f314a0e027b6f9489d4687 @@ -3983,7 +3996,7 @@ SPEC CHECKSUMS: ABI43_0_0EXNotifications: cdba9427d397cc08a00e566f5aeb246c9f86f6ef ABI43_0_0EXPermissions: 8f92f44003f1cad2ae59b8942ad53fda59253e43 ABI43_0_0Expo: ebfd83300ee9f09f3e25c10b7e6ccd43973d18d8 - ABI43_0_0ExpoKit: 0a4613447bbbb226cbc00a16f04f0009e245a474 + ABI43_0_0ExpoKit: 2567a3e2abf46e828094943b8e5e40e7f4a485a8 ABI43_0_0ExpoModulesCore: 21516b2200164b3c291fe3f039858a8a973dbd2c ABI43_0_0ExpoModulesProvider: ef64cd037ca8a47d71183d752a079a4e04504043 ABI43_0_0EXPrint: 962141f476b980b2e48a9a603bdd910d66ba2f50 @@ -4007,17 +4020,17 @@ SPEC CHECKSUMS: ABI43_0_0EXVideoThumbnails: f9d30f4430fd73bf5d0550876c39be39165837b0 ABI43_0_0EXWebBrowser: fa11039b71b6c1e34d422c4c15fe5064d7234430 ABI43_0_0FBLazyVector: d863687790aab2335c29f048d87d1691a25b7ea6 - ABI43_0_0FBReactNativeSpec: d7e93ec685ef8dca57c78f567f86913e3c413332 + ABI43_0_0FBReactNativeSpec: 7825740bd4be322b8a1aba23bd5848934af79ddc ABI43_0_0lottie-react-native: ae64d0ef211f368a4b16e937b061670e6a9e18c6 ABI43_0_0RCTRequired: c68a49d88705881ee9fec43d6665f4994fa22bf3 - ABI43_0_0RCTTypeSafety: 79a0f62fe3491a45a62273e8d32c17e1c698ab6a + ABI43_0_0RCTTypeSafety: 9cc02847084b2bc1e17d6818d5d3e6aeb46f7fb3 ABI43_0_0React: 1d05a91bcbc26919d7c63fe0ec8123d271b0659f ABI43_0_0React-callinvoker: d336978aea9027a1025207901d2d58cd6e88a125 - ABI43_0_0React-Core: f97f72e626712fd56ba0a121983cba5223530b79 - ABI43_0_0React-CoreModules: a2ab8fc6be9b30d2336f668fde9be5f8901e82b1 - ABI43_0_0React-cxxreact: 9d306c28a0452dfdb1941f598e953fb7bbfd0572 - ABI43_0_0React-jsi: e43fbe1cae7e4fd97dfb2e1e5ceaf701f215d659 - ABI43_0_0React-jsiexecutor: ede515265e21f4d8c45d8d6c14dec35e16102506 + ABI43_0_0React-Core: 95a21d72716182ecab44b5de2149ba862cc55dc3 + ABI43_0_0React-CoreModules: 3026df87662c63ce5846b1e05e87fedd7788fb8c + ABI43_0_0React-cxxreact: dce98b856942dc30ab1dad3487d20e31db01b62b + ABI43_0_0React-jsi: 10ad77e3525730d7443a7d646ea93c679a4bda99 + ABI43_0_0React-jsiexecutor: 8f82f13c5f85a3fa4ccd06f6477656608c824de8 ABI43_0_0React-jsinspector: 6e1281d23a460871667061a088c2529a0366c483 ABI43_0_0react-native-netinfo: a175663a23a03b61d8028abfaa7d30b2b0bb5576 ABI43_0_0react-native-pager-view: f158cec39be2913340e88ec31cfe1b163f8577da @@ -4025,17 +4038,17 @@ SPEC CHECKSUMS: ABI43_0_0react-native-webview: 8a19e64ee814a441110d24ffe8d9165c9eaac949 ABI43_0_0React-perflogger: 4dbcbe33dcb01b5ebe1d73e1468cc88e698e5c8f ABI43_0_0React-RCTActionSheet: 82a309e9d9269d62084601726024e10a8b222d8d - ABI43_0_0React-RCTAnimation: 7fb2cac255830f2b1703eb04cb78fbd4bb2acc21 - ABI43_0_0React-RCTBlob: 5fa75eb2025f14b8fa7ebfc41245b7edb0755a3e - ABI43_0_0React-RCTImage: ed7857774747b7a694af30748cddf081a91939f1 + ABI43_0_0React-RCTAnimation: cd27cdc6326d76865bd6a4ae05ae7370e585bda0 + ABI43_0_0React-RCTBlob: 4185a91d9e1ca22f90a4d592de5b836973726274 + ABI43_0_0React-RCTImage: c99427e452297af8b5ffcfa7604e51e84b1b04fa ABI43_0_0React-RCTLinking: 2af9542c231014c153a5a72498af771e4c2cb448 - ABI43_0_0React-RCTNetwork: cf0820015ff44ad6577a4d0a70a36fc6605968d4 - ABI43_0_0React-RCTSettings: d7bbc9e4c91c2c4631e7c59e00952bdc5e45f5bd + ABI43_0_0React-RCTNetwork: d7eafc5e5cf3780c1f41b9ea0f6adadd6b4efa1e + ABI43_0_0React-RCTSettings: 75611688b060b59a589a729b2c87fcca1032f9f9 ABI43_0_0React-RCTText: e2134ddfd51d7c4e9048313d5277afdff4fc5e88 - ABI43_0_0React-RCTVibration: 463dbb8cdfaa37bfe093c5003fb409194ea9202d + ABI43_0_0React-RCTVibration: 075685f24e2ff498382dffaf3e5a5fecddd1e811 ABI43_0_0React-runtimeexecutor: 338ed29ff7b8856c96a8e6d5578f201054a5f320 - ABI43_0_0ReactCommon: 6a1fcad151aa7efad288c20f318c1b5f165538d2 - ABI43_0_0RNReanimated: f7cd484a2e1b5112ea84d57381c5d7ab6ceeab74 + ABI43_0_0ReactCommon: bfa3173903969ba34ab2930eeb4cb2b4225eb914 + ABI43_0_0RNReanimated: c08a0d8e39fa8cd455c2c685e69aa0e84c820ea4 ABI43_0_0RNScreens: d8e359af1191871e37501d5efd6b72b38c573e90 ABI43_0_0stripe-react-native: e2d9c45776c547095508a9b8b5173e8a34bb962a ABI43_0_0UMAppLoader: ac4c403f59e691af683873f22b44a023155bac55 @@ -4086,7 +4099,7 @@ SPEC CHECKSUMS: ABI44_0_0Expo: e45569be42bf2d520b942880591eeeabdcaa0053 ABI44_0_0ExpoCellular: 3e8fc3b142ae7f31ddb69e69edf199112faf0770 ABI44_0_0ExpoHaptics: 2f68632330f572a21bdb4e0019c55549ab0a6be2 - ABI44_0_0ExpoKit: f45a116c8936e10db376b5379795f5fad74d38dc + ABI44_0_0ExpoKit: 08ecf8e9dfa8764cb2ec1e7feb2723bf3f94ed8b ABI44_0_0ExpoLinearGradient: 936a89231f2b1ca7c96099ca9afc02213c86bdd0 ABI44_0_0ExpoLocalization: 4e89d45edff085969c2a682e92a20411563a507b ABI44_0_0ExpoModulesCore: 950aefa03562dca8ad6f43aac2eaa5151daacf45 @@ -4113,17 +4126,17 @@ SPEC CHECKSUMS: ABI44_0_0EXVideoThumbnails: 002faa2841bf70244f766330630fd6cf3b2f1f76 ABI44_0_0EXWebBrowser: 5f4a34c63ea57bac9c4f2fabc0db4cda7f41203c ABI44_0_0FBLazyVector: 4fc12a7db0b6f161e2882dcf0a0ca74a31a9558e - ABI44_0_0FBReactNativeSpec: ca24dad9fa568d8261116ca16071a496a8a64b54 + ABI44_0_0FBReactNativeSpec: a343114505d1d363bcfd9d80917ec83fabf401bd ABI44_0_0lottie-react-native: 6d76733dbbb0d4dcde1c98d88a2b5ab91e5d835e ABI44_0_0RCTRequired: 1a8fac6ea7a9bcc91d362a05037cfdd3c7b52b5c - ABI44_0_0RCTTypeSafety: 0a6636140c832ed98fcf20eb09d34a26f618b41f + ABI44_0_0RCTTypeSafety: a15982b051ebf6b24d345ee2d87575dac1fa8497 ABI44_0_0React: 702a6f0378cec107db9655d2234bf19c0f4c36fd ABI44_0_0React-callinvoker: 78093d78fd956e932c34bee9efba34e31c238c9a - ABI44_0_0React-Core: d4bef903c8ba37a511a56886aac35569eaa9d4fa - ABI44_0_0React-CoreModules: bceb581f8cae21dc5393ce874f86ba5b2e937a88 - ABI44_0_0React-cxxreact: d91e3eab6c66928250b65f6b351d2577a1291f9e - ABI44_0_0React-jsi: dfe71fc71ee20db5a2c557f87d99ba7845cbd125 - ABI44_0_0React-jsiexecutor: 8222779637f61d188d333d78ae09c546f74e47d7 + ABI44_0_0React-Core: 6ee8574fdaf67fdb21b807651f5c661a41bda5e8 + ABI44_0_0React-CoreModules: 8bef16252197922145388e690b692b5f54dae6b3 + ABI44_0_0React-cxxreact: 3cca62018e3ce010c7fb1fb3d1ec3b24dc8fca86 + ABI44_0_0React-jsi: 005e0e96701f5e07ddd5872a2e02b7fbb3ad7d7d + ABI44_0_0React-jsiexecutor: 2d3e71aff30ca31d9e0971ad3365820c72a21231 ABI44_0_0React-jsinspector: 2145869a3fdeeb44dc632a563a3cba332dcf0c9c ABI44_0_0react-native-netinfo: e40f6a99494a460814af6b2dd7ffd1be2effd0d2 ABI44_0_0react-native-pager-view: 285de72b6b60da4b73689c9901a519c961428298 @@ -4131,16 +4144,16 @@ SPEC CHECKSUMS: ABI44_0_0react-native-webview: 45654397d942312456b5d07d331ee9b2f539ac98 ABI44_0_0React-perflogger: 2a951471d4c861218a24111a0f570d22b6cb4107 ABI44_0_0React-RCTActionSheet: f665f8468e98a71777705215309683580b0f7051 - ABI44_0_0React-RCTAnimation: 15f26c0f1ca6fbe11f3daaa67216a0ee626951b4 - ABI44_0_0React-RCTBlob: d98a805ce3a161fbbb8944bd7053cfbaf3f1a748 - ABI44_0_0React-RCTImage: 8efbac6d7ee0c4a068f571da39918592630b80cd + ABI44_0_0React-RCTAnimation: 46331635cf37ee6ccf601cb5ab3608c101f5734c + ABI44_0_0React-RCTBlob: 98ff152e9e195062adaeca32a6a3f510715ad778 + ABI44_0_0React-RCTImage: 16dd6c30ce61b634c269d6c5b34d6df234760acd ABI44_0_0React-RCTLinking: 5a6c295e33d2d45f7b2f84c30236655488401576 - ABI44_0_0React-RCTNetwork: 54ff03766eed57e2e0baecbeacebf2c90b381f72 - ABI44_0_0React-RCTSettings: f7b75c05c612da3ca94e82071eef2f479788afa7 + ABI44_0_0React-RCTNetwork: e68201e743782a4518003e8987a47634717c609f + ABI44_0_0React-RCTSettings: f0ba3152ce2d684eead1e9bb16abec3bed159df8 ABI44_0_0React-RCTText: 94b27e12f3acc8ab377796135f763b493df3c566 - ABI44_0_0React-RCTVibration: 244d005cb3b6b2cf2b42da84d3821d2e9f063b92 + ABI44_0_0React-RCTVibration: 861a9c0c0219bfe94c95a59f7770b53bc9ab19ae ABI44_0_0React-runtimeexecutor: e896ce310401b7a6c55c8e9ebd05d329425704f0 - ABI44_0_0ReactCommon: 57cd9920705425d6d7979326c5f755a7134612d0 + ABI44_0_0ReactCommon: c2df8ddaa1d7820cf621c4db5462e4b943b88ce2 ABI44_0_0RNGestureHandler: 0ebf658b94a2583f265cd1fc02a50adf7db6f611 ABI44_0_0RNReanimated: 5a9c79f77d68cc558867f38dbd78efe7c6e328ec ABI44_0_0RNScreens: 7a62e5f04bfaf607a3927152dbf4efd3fcf2dc8a @@ -4152,10 +4165,10 @@ SPEC CHECKSUMS: Analytics: 231b01fdaa9704105881dc82def7b7b1d6571edb AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7 ASN1Decoder: 6110fdeacfdb41559b1481457a1645be716610aa - boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c + boost: a7c83b31436843459a1961bfd74b96033dc77234 Branch: 9a37f707974a128c37829033c49018b79c7e7a2d CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947 - DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de + DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 EXAdsAdMob: ac13d1a8fd689e3ac74b63100b13d24f3ea0ff1f EXAdsFacebook: 9697b91f55d254ebe4c3a1ddba9c76dfa8302775 EXAmplitude: f346f64108b628046524182a8cc30df407643dc9 @@ -4229,25 +4242,26 @@ SPEC CHECKSUMS: EXVideoThumbnails: 847d648d6f4bc0c1afad05caa56a487dc543445e FacebookSDK: 4b9bb8e2824898b47f18c666dc145c09b40609e6 FBAudienceNetwork: cfe55330dcc4e9a1df083c34a346ca7f8e32951e - FBLazyVector: c71c5917ec0ad2de41d5d06a5855f6d5eda06971 - FBReactNativeSpec: 289039c27ca26d02a6683c56c6fc7868bc804c43 + FBLazyVector: 244195e30d63d7f564c55da4410b9a24e8fbceaa + FBReactNativeSpec: 2e2033e9dfdc3d463113e4dbdc03e332d7289dd4 FBSDKCoreKit: dace5abafc2fd9272733e6d027bcf18102712117 Firebase: cd2ab85eec8170dc260186159f21072ecb679ad5 FirebaseAnalytics: f3f8f75de34fe04141a69bb1c4bd7e24a80178e1 FirebaseCore: ac35d680a0bf32319a59966a1478e0741536b97b FirebaseCoreDiagnostics: 3d36e05da74cb8b7ce30e6594a8f201b982c725c FirebaseInstallations: a58d4f72ec5861840b84df489f2668d970df558a - glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 + fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 + glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85 Google-Maps-iOS-Utils: c32891ff472eaaa1fca032beedafa1a013af7875 Google-Mobile-Ads-SDK: 2f288748a42920d1c744946a460896a95b0e9110 GoogleAppMeasurement: 0c3b134b2c0a90c4c24833873894bfe0e42a0384 GoogleDataTransport: 8b0e733ea77c9218778e5a9e34ba9508b8328939 - GoogleMaps: 82f8fe307193bb4de6ef6bf9e88f1d624b718f9d + GoogleMaps: 8c080862ff2748ece9929471b30feb56a6c5079d GoogleMLKit: 6ca2a10de262ee1017b52ac045e8967884ace992 GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213 GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34 GoogleUserMessagingPlatform: b168e8c46cd8f92aa3e34b584c4ca78a411ce367 - GoogleUtilities: 684ee790a24f73ebb2d1d966e9711c203f2a4237 + GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89 GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91 @@ -4265,35 +4279,36 @@ SPEC CHECKSUMS: PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 Protobuf: 3724efa50cb2846d7ccebc8691c574e85fd74471 Quick: 5dc45f9bc11236594a7acc99f7bd85ecdc9a477d - RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c - RCTRequired: d34bf57e17cb6e3b2681f4809b13843c021feb6c - RCTTypeSafety: 8dab4933124ed39bb0c1d88d74d61b1eb950f28f - React: ef700aeb19afabff83a9cc5799ac955a9c6b5e0f - React-callinvoker: 5547633d44f3e114b17c03c660ccb5faefd9ed2d - React-Core: 3858d60185d71567962468bf176d582e36e4e25b - React-CoreModules: 29b3397adac0c04915cf93089328664868510717 - React-cxxreact: 7e6cc1f4cdfcd40e483dd228fa8a3d3e0ed16f4a - React-jsi: a8b09c29521c798f1783348b37b511ba7b3dbeb3 - React-jsiexecutor: df6abc9fafbecb8e5b7a5fbc5e6d4bd017d594d5 - React-jsinspector: 34e23860273a23695342f58eed3ffd3ba10c31e0 + RCT-Folly: 803a9cfd78114b2ec0f140cfa6fa2a6bafb2d685 + RCTRequired: cd47794163052d2b8318c891a7a14fcfaccc75ab + RCTTypeSafety: 393bb40b3e357b224cde53d3fec26813c52428b1 + React: dec6476bc27155b250eeadfc11ea779265f53ebf + React-callinvoker: e5047929e80aea942e6fdd96482504ef0189ca63 + React-Core: e382655566b2b9a6e3b4f641d777b7bfdbe52358 + React-CoreModules: cf262e82fa101c0aee022b6f90d1a5b612038b64 + React-cxxreact: 69d53de3b30c7c161ba087ca1ecdffed9ccb1039 + React-jsi: ce9a2d804adf75809ce2fe2374ba3fbbf5d59b03 + React-jsiexecutor: 52beb652bbc61201bd70cbe4f0b8edb607e8da4f + React-jsinspector: 595f76eba2176ebd8817a1fffd47b84fbdab9383 + React-logger: 23de8ea0f44fa00ee77e96060273225607fd4d78 react-native-netinfo: 42c0965fca99069b92e3f7360ab2d425985e5104 react-native-pager-view: 3ee7d4c7697fb3ef788346e834a60cca97ed8540 react-native-segmented-control: 06607462630512ff8eef652ec560e6235a30cc3e react-native-webview: e89bf2dba26a04cda967814df3ed1be99f291233 - React-perflogger: cc76a4254d19640f1d8ad1c66fdee800414b805c - React-RCTActionSheet: 7448f049318d8d7e8a9a1ebb742ada721757eea8 - React-RCTAnimation: fb9b3fa1a4a9f5e6ab01b3368693ce69860ba76a - React-RCTBlob: a2e7056601c599c19884992f08ebacae810426f9 - React-RCTImage: 5a46c12327d0d6f6844a1fe38baa92a1e02847e8 - React-RCTLinking: 63dd8305591e1def35267557ed42918aec9eb30b - React-RCTNetwork: d0516e39a5f736b2bff671c3e03804200161dcd3 - React-RCTSettings: a09566b14f1649f6c8a39ad1a174bb5c0631bb09 - React-RCTText: 04a2f0a281f715f0aed4f515717fafd64510e2c8 - React-RCTVibration: c7f845861e79eae13dc1e8217a3cf47a3945b504 - React-runtimeexecutor: 493d9abb8b23c3f84e19ae221eeba92cadcb70dc - ReactCommon: 8fea6422328e2fc093e25c9fac67adbcf0f04fb4 + React-perflogger: 3c9bb7372493e49036f07a82c44c8cf65cbe88db + React-RCTActionSheet: 052606483045a408693aa7e864410b4a052f541a + React-RCTAnimation: 08d4cac13222bb1348c687a0158dfd3b577cdb63 + React-RCTBlob: 928ad1df65219c3d9e2ac80983b943a75b5c3629 + React-RCTImage: 524d7313b142a39ee0e20fa312b67277917fe076 + React-RCTLinking: 44036ea6f13a2e46238be07a67566247fee35244 + React-RCTNetwork: 9b6faacf1e0789253e319ca53b1f8d92c2ac5455 + React-RCTSettings: ecd8094f831130a49581d5112a8607220e5d12a5 + React-RCTText: 14ba976fb48ed283cfdb1a754a5d4276471e0152 + React-RCTVibration: 99c7f67fba7a5ade46e98e870c6ff2444484f995 + React-runtimeexecutor: 2450b43df7ffe8e805a0b3dcb2abd4282f1f1836 + ReactCommon: d98c6c96b567f9b3a15f9fd4cc302c1eda8e3cf2 RNGestureHandler: e5c7cab5f214503dcefd6b2b0cefb050e1f51c4a - RNReanimated: 7f143675d94978c3555345d5bfa8ed6ae800c5d3 + RNReanimated: 1326679461fa5d2399d54c18ca1432ba3e816b9e RNScreens: 522705f2e5c9d27efb17f24aceb2bf8335bc7b8e Stripe: 22c1b8da5ee20a1aaf40fd198160efa72e71644a stripe-react-native: a5fcf07b49f1208bdc939e31a07320b35a88209f @@ -4301,9 +4316,9 @@ SPEC CHECKSUMS: StripeUICore: 7edf64b24c9c178bfd97e988f2cffdbb7a7628b0 UMAppLoader: fd7c70dca4bbb1769564022cb0b3e4ad440da3e6 UMTaskManagerInterface: a06f988b2a16a445d55944b090390c76d3be2407 - Yoga: e6ecf3fa25af9d4c87e94ad7d5d292eedef49749 + Yoga: 9b6696970c3289e8dea34b3eda93f23e61fb8121 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: fe12df20c65765b89a1ff91df5c8ae644d695e2b +PODFILE CHECKSUM: eea86c1c97b467d13bd2bd42d819c7d4eac5162c COCOAPODS: 1.11.2 diff --git a/ios/vendored/sdk42/react-native-reanimated/ABI42_0_0RNReanimated.podspec.json b/ios/vendored/sdk42/react-native-reanimated/ABI42_0_0RNReanimated.podspec.json index 812be05b9ba69..b4568962e0225 100644 --- a/ios/vendored/sdk42/react-native-reanimated/ABI42_0_0RNReanimated.podspec.json +++ b/ios/vendored/sdk42/react-native-reanimated/ABI42_0_0RNReanimated.podspec.json @@ -26,12 +26,12 @@ ], "pod_target_xcconfig": { "USE_HEADERMAP": "YES", - "HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ABI42_0_0ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\" " + "HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ABI42_0_0ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\" " }, "compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=63 -Wno-comma -Wno-shorten-64-to-32 -Wno-documentation", "xcconfig": { "CLANG_CXX_LANGUAGE_STANDARD": "c++14", - "HEADER_SEARCH_PATHS": "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", + "HEADER_SEARCH_PATHS": "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", "OTHER_CFLAGS": "$(inherited) -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=63" }, "requires_arc": true, diff --git a/ios/vendored/sdk43/react-native-reanimated/ABI43_0_0RNReanimated.podspec.json b/ios/vendored/sdk43/react-native-reanimated/ABI43_0_0RNReanimated.podspec.json index 756d8489db914..3aaf6b5acadfe 100644 --- a/ios/vendored/sdk43/react-native-reanimated/ABI43_0_0RNReanimated.podspec.json +++ b/ios/vendored/sdk43/react-native-reanimated/ABI43_0_0RNReanimated.podspec.json @@ -26,12 +26,12 @@ ], "pod_target_xcconfig": { "USE_HEADERMAP": "YES", - "HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ABI43_0_0ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\" " + "HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ABI43_0_0ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\" " }, "compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=64 -Wno-comma -Wno-shorten-64-to-32 -Wno-documentation", "xcconfig": { "CLANG_CXX_LANGUAGE_STANDARD": "c++14", - "HEADER_SEARCH_PATHS": "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", + "HEADER_SEARCH_PATHS": "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", "OTHER_CFLAGS": "$(inherited) -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=64" }, "requires_arc": true, diff --git a/ios/vendored/unversioned/react-native-reanimated/RNReanimated.podspec.json b/ios/vendored/unversioned/react-native-reanimated/RNReanimated.podspec.json index 55ba43f84462e..d0bb384cecda9 100644 --- a/ios/vendored/unversioned/react-native-reanimated/RNReanimated.podspec.json +++ b/ios/vendored/unversioned/react-native-reanimated/RNReanimated.podspec.json @@ -28,11 +28,11 @@ "USE_HEADERMAP": "YES", "HEADER_SEARCH_PATHS": "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " }, - "compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=64 -Wno-comma -Wno-shorten-64-to-32 -Wno-documentation", + "compiler_flags": "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=67 -Wno-comma -Wno-shorten-64-to-32 -Wno-documentation", "xcconfig": { "CLANG_CXX_LANGUAGE_STANDARD": "c++14", "HEADER_SEARCH_PATHS": "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"${PODS_ROOT}/Headers/Public/React-hermes\" \"${PODS_ROOT}/Headers/Public/hermes-engine\"", - "OTHER_CFLAGS": "$(inherited) -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=64" + "OTHER_CFLAGS": "$(inherited) -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DRNVERSION=67" }, "requires_arc": true, "dependencies": { diff --git a/ios/versioned-react-native/ABI42_0_0/Expo/ExpoKit/ABI42_0_0ExpoKit.podspec b/ios/versioned-react-native/ABI42_0_0/Expo/ExpoKit/ABI42_0_0ExpoKit.podspec index f3d78d5cf2481..ad9493d383289 100644 --- a/ios/versioned-react-native/ABI42_0_0/Expo/ExpoKit/ABI42_0_0ExpoKit.podspec +++ b/ios/versioned-react-native/ABI42_0_0/Expo/ExpoKit/ABI42_0_0ExpoKit.podspec @@ -4,7 +4,7 @@ folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1' folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -21,7 +21,7 @@ Pod::Spec.new do |s| s.source = { :git => "http://github.com/expo/expo.git" } s.xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++14', - 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", + 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", 'OTHER_CPLUSPLUSFLAGS' => [ "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -33,11 +33,11 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", - "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " } s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.xcconfig = { - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\"", + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\"", "OTHER_CFLAGS" => "$(inherited)" + " " + folly_flags } diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ABI42_0_0React-Core.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ABI42_0_0React-Core.podspec index e5bf05afa3776..92a4d4c8a755e 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ABI42_0_0React-Core.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ABI42_0_0React-Core.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' header_subspecs = { @@ -43,7 +43,7 @@ Pod::Spec.new do |s| s.header_dir = "ABI42_0_0React" s.framework = "JavaScriptCore" s.library = "stdc++" - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Folly\"", "DEFINES_MODULE" => "YES" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\"", "DEFINES_MODULE" => "YES" } s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\""} s.default_subspec = "Default" diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Blob/ABI42_0_0React-RCTBlob.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Blob/ABI42_0_0React-RCTBlob.podspec index d2674337d0332..dc7ce09293f90 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Blob/ABI42_0_0React-RCTBlob.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Blob/ABI42_0_0React-RCTBlob.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTBlob" @@ -29,7 +29,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "RCT-Folly", folly_version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/FBReactNativeSpec/ABI42_0_0FBReactNativeSpec.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/FBReactNativeSpec/ABI42_0_0FBReactNativeSpec.podspec index 5108771cfb8d9..ea860e574f829 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/FBReactNativeSpec/ABI42_0_0FBReactNativeSpec.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/FBReactNativeSpec/ABI42_0_0FBReactNativeSpec.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0FBReactNativeSpec" @@ -29,7 +29,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/Libraries/ABI42_0_0FBReactNativeSpec\" \"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/Libraries/ABI42_0_0FBReactNativeSpec\" \"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "RCT-Folly", folly_version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Image/ABI42_0_0React-RCTImage.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Image/ABI42_0_0React-RCTImage.podspec index aa6817e287565..4ef785504aa73 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Image/ABI42_0_0React-RCTImage.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Image/ABI42_0_0React-RCTImage.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTImage" @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "RCT-Folly", folly_version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/LinkingIOS/ABI42_0_0React-RCTLinking.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/LinkingIOS/ABI42_0_0React-RCTLinking.podspec index 623915d4d4088..4fa54bcacd7b3 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/LinkingIOS/ABI42_0_0React-RCTLinking.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/LinkingIOS/ABI42_0_0React-RCTLinking.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTLinking" @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "ABI42_0_0FBReactNativeSpec", version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/NativeAnimation/ABI42_0_0React-RCTAnimation.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/NativeAnimation/ABI42_0_0React-RCTAnimation.podspec index fcd50310c5e83..73a2ada2522b7 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/NativeAnimation/ABI42_0_0React-RCTAnimation.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/NativeAnimation/ABI42_0_0React-RCTAnimation.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTAnimation" @@ -29,7 +29,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "RCT-Folly", folly_version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Network/ABI42_0_0React-RCTNetwork.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Network/ABI42_0_0React-RCTNetwork.podspec index a638e49180709..84122a7031261 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Network/ABI42_0_0React-RCTNetwork.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Network/ABI42_0_0React-RCTNetwork.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTNetwork" @@ -29,7 +29,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.frameworks = "MobileCoreServices" diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/PushNotificationIOS/ABI42_0_0React-RCTPushNotification.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/PushNotificationIOS/ABI42_0_0React-RCTPushNotification.podspec index 0e9a84534c8f2..c3bfb6a266d34 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/PushNotificationIOS/ABI42_0_0React-RCTPushNotification.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/PushNotificationIOS/ABI42_0_0React-RCTPushNotification.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTPushNotification" @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.framework = "UserNotifications" diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Settings/ABI42_0_0React-RCTSettings.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Settings/ABI42_0_0React-RCTSettings.podspec index 5b4d99f00fbea..80fc26fcf6b3a 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Settings/ABI42_0_0React-RCTSettings.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Settings/ABI42_0_0React-RCTSettings.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTSettings" @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "RCT-Folly", folly_version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/TypeSafety/ABI42_0_0RCTTypeSafety.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/TypeSafety/ABI42_0_0RCTTypeSafety.podspec index 3de6ddc960a0f..e00a252937278 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/TypeSafety/ABI42_0_0RCTTypeSafety.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/TypeSafety/ABI42_0_0RCTTypeSafety.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0RCTTypeSafety" @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/Libraries/TypeSafety\" \"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/Libraries/TypeSafety\" \"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "ABI42_0_0FBLazyVector", version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Vibration/ABI42_0_0React-RCTVibration.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Vibration/ABI42_0_0React-RCTVibration.podspec index b908ee7fabdce..f5d71fa523989 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Vibration/ABI42_0_0React-RCTVibration.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/Libraries/Vibration/ABI42_0_0React-RCTVibration.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-RCTVibration" @@ -30,7 +30,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/RCT-Folly\"" } s.frameworks = "AudioToolbox" diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/React/ABI42_0_0React-RCTFabric.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/React/ABI42_0_0React-RCTFabric.podspec index 9fa16e2d4f7d2..22128b6154e8a 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/React/ABI42_0_0React-RCTFabric.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/React/ABI42_0_0React-RCTFabric.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1' folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -31,8 +31,8 @@ Pod::Spec.new do |s| s.header_dir = "ABI42_0_0React" s.framework = "JavaScriptCore" s.library = "stdc++" - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" } - s.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/Folly\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" } + s.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT\)\/RCT-Folly\"", "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED" + " " + folly_flags } s.dependency "ABI42_0_0React-Core", version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/React/CoreModules/ABI42_0_0React-CoreModules.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/React/CoreModules/ABI42_0_0React-CoreModules.podspec index dbb320ed7a56e..7e2cc36d7ea78 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/React/CoreModules/ABI42_0_0React-CoreModules.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/React/CoreModules/ABI42_0_0React-CoreModules.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-CoreModules" @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14", - "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/React/CoreModules\" \"$(PODS_ROOT)/Folly\"" + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/React/CoreModules\" \"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "ABI42_0_0FBReactNativeSpec", version diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/React/third-party.xcconfig b/ios/versioned-react-native/ABI42_0_0/ReactNative/React/third-party.xcconfig index ada55f5ebe8ce..683e73b1ebb44 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/React/third-party.xcconfig +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/React/third-party.xcconfig @@ -8,5 +8,5 @@ // LICENSE file in the root directory of this source tree. // -HEADER_SEARCH_PATHS = $(SRCROOT)/../third-party/boost_1_63_0 $(SRCROOT)/../third-party/folly-2020.01.13.00 $(SRCROOT)/../third-party/glog-0.3.5/src +HEADER_SEARCH_PATHS = $(SRCROOT)/../third-party/boost_1_63_0 $(SRCROOT)/../third-party/folly-2021.06.28.00 $(SRCROOT)/../third-party/glog-0.3.5/src OTHER_CFLAGS = -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0React-Fabric.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0React-Fabric.podspec index d4a2be1185cef..f75e70ffca21a 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0React-Fabric.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0React-Fabric.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' folly_dep_name = 'RCT-Folly/Fabric' boost_compiler_flags = '-Wno-documentation' @@ -44,7 +44,7 @@ Pod::Spec.new do |s| ss.source_files = "fabric/attributedstring/**/*.{m,mm,cpp,h}" ss.exclude_files = "**/tests/*" ss.header_dir = "ABI42_0_0react/attributedstring" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "better" do |ss| @@ -53,7 +53,7 @@ Pod::Spec.new do |s| ss.source_files = "better/**/*.{m,mm,cpp,h}" ss.exclude_files = "**/tests/*" ss.header_dir = "ABI42_0_0better" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "config" do |ss| @@ -68,7 +68,7 @@ Pod::Spec.new do |s| ss.source_files = "fabric/core/**/*.{m,mm,cpp,h}" ss.exclude_files = "**/tests/**/*" ss.header_dir = "ABI42_0_0react/core" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "components" do |ss| @@ -78,7 +78,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/activityindicator/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/activityindicator" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "image" do |sss| @@ -87,7 +87,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/image/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/image" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "modal" do |sss| @@ -96,7 +96,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/modal/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/modal" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "rncore" do |sss| @@ -105,7 +105,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/rncore/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*", "fabric/components/rncore/*Tests.{h,cpp}" sss.header_dir = "ABI42_0_0react/components/rncore" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "root" do |sss| @@ -114,7 +114,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/root/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/root" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "scrollview" do |sss| @@ -123,7 +123,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/scrollview/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/scrollview" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "slider" do |sss| @@ -133,7 +133,7 @@ Pod::Spec.new do |s| sss.exclude_files = "**/tests/*", "**/android/*" sss.header_dir = "ABI42_0_0react/components/slider" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "text" do |sss| @@ -142,7 +142,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/text/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/text" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end ss.subspec "view" do |sss| @@ -152,7 +152,7 @@ Pod::Spec.new do |s| sss.source_files = "fabric/components/view/**/*.{m,mm,cpp,h}" sss.exclude_files = "**/tests/*" sss.header_dir = "ABI42_0_0react/components/view" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end end @@ -162,7 +162,7 @@ Pod::Spec.new do |s| ss.source_files = "fabric/debug/**/*.{m,mm,cpp,h}" ss.exclude_files = "**/tests/*" ss.header_dir = "ABI42_0_0react/debug" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "imagemanager" do |ss| @@ -174,7 +174,7 @@ Pod::Spec.new do |s| "**/android/*", "**/cxx/*" ss.header_dir = "ABI42_0_0react/imagemanager" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "mounting" do |ss| @@ -183,7 +183,7 @@ Pod::Spec.new do |s| ss.source_files = "fabric/mounting/**/*.{m,mm,cpp,h}" ss.exclude_files = "**/tests/*" ss.header_dir = "ABI42_0_0react/mounting" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "textlayoutmanager" do |ss| @@ -194,7 +194,7 @@ Pod::Spec.new do |s| "**/android/*", "**/cxx/*" ss.header_dir = "ABI42_0_0react/textlayoutmanager" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "uimanager" do |ss| @@ -203,12 +203,12 @@ Pod::Spec.new do |s| ss.source_files = "fabric/uimanager/**/*.{m,mm,cpp,h}" ss.exclude_files = "**/tests/*", ss.header_dir = "ABI42_0_0react/uimanager" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end s.subspec "utils" do |ss| ss.source_files = "utils/*.{m,mm,cpp,h}" ss.header_dir = "ABI42_0_0react/utils" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } end end diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0ReactCommon.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0ReactCommon.podspec index ee08092c116b5..80ba231fe8f38 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0ReactCommon.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/ABI42_0_0ReactCommon.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.source = { :path => "." } s.header_dir = "ABI42_0_0ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI42_0_0React-Core\"", "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" } diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/callinvoker/ABI42_0_0React-callinvoker.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/callinvoker/ABI42_0_0React-callinvoker.podspec index dfa758b538b72..626a164861480 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/callinvoker/ABI42_0_0React-callinvoker.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/callinvoker/ABI42_0_0React-callinvoker.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/cxxreact/ABI42_0_0React-cxxreact.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/cxxreact/ABI42_0_0React-cxxreact.podspec index 174e30d5def73..d7726aa60b969 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/cxxreact/ABI42_0_0React-cxxreact.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/cxxreact/ABI42_0_0React-cxxreact.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -26,10 +26,10 @@ Pod::Spec.new do |s| s.source_files = "*.{cpp,h}" s.exclude_files = "ABI42_0_0SampleCxxModule.*" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI42_0_0cxxreact" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/fabric/graphics/ABI42_0_0React-graphics.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/fabric/graphics/ABI42_0_0React-graphics.podspec index 914d7d5ef5b26..5e503e475b633 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/fabric/graphics/ABI42_0_0React-graphics.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/fabric/graphics/ABI42_0_0React-graphics.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI42_0_0React-graphics" @@ -29,7 +29,7 @@ Pod::Spec.new do |s| "**/android/*", "**/cxx/*" s.header_dir = "ABI42_0_0react/graphics" - s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" } + s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/RCT-Folly\"" } s.dependency "RCT-Folly/Fabric", folly_version end diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsi/ABI42_0_0React-jsi.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsi/ABI42_0_0React-jsi.podspec index d4f087c6a2ab8..70e20345bb696 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsi/ABI42_0_0React-jsi.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsi/ABI42_0_0React-jsi.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,11 +27,11 @@ Pod::Spec.new do |s| s.exclude_files = "**/test/*" s.framework = "JavaScriptCore" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI42_0_0jsi" s.default_subspec = "Default" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsiexecutor/ABI42_0_0React-jsiexecutor.podspec b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsiexecutor/ABI42_0_0React-jsiexecutor.podspec index 1a50f47b9c288..190021024e03e 100644 --- a/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsiexecutor/ABI42_0_0React-jsiexecutor.podspec +++ b/ios/versioned-react-native/ABI42_0_0/ReactNative/ReactCommon/jsiexecutor/ABI42_0_0React-jsiexecutor.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -25,7 +25,7 @@ Pod::Spec.new do |s| s.source = { :path => "." } s.source_files = "jsireact/*.{cpp,h}" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI42_0_0jsireact" s.dependency "ABI42_0_0React-cxxreact", version diff --git a/ios/versioned-react-native/ABI43_0_0/Expo/ExpoKit/ABI43_0_0ExpoKit.podspec b/ios/versioned-react-native/ABI43_0_0/Expo/ExpoKit/ABI43_0_0ExpoKit.podspec index 545f39406a4c3..41a77a32ffcc0 100644 --- a/ios/versioned-react-native/ABI43_0_0/Expo/ExpoKit/ABI43_0_0ExpoKit.podspec +++ b/ios/versioned-react-native/ABI43_0_0/Expo/ExpoKit/ABI43_0_0ExpoKit.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |s| s.source = { :git => "http://github.com/expo/expo.git" } s.xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++14', - 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", + 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", 'OTHER_CPLUSPLUSFLAGS' => [ "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -32,11 +32,11 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", - "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " } s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.xcconfig = { - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\"", + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\"", "OTHER_CFLAGS" => "$(inherited)" + " " + folly_flags } diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ABI43_0_0React-Core.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ABI43_0_0React-Core.podspec index b4ed9ffb3d87d..3a81ebf5c8ebd 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ABI43_0_0React-Core.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ABI43_0_0React-Core.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' header_subspecs = { @@ -42,7 +42,7 @@ Pod::Spec.new do |s| s.header_dir = "ABI43_0_0React" s.framework = "JavaScriptCore" s.library = "stdc++" - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\"", "DEFINES_MODULE" => "YES" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\"", "DEFINES_MODULE" => "YES" } s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\""} s.default_subspec = "Default" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Blob/ABI43_0_0React-RCTBlob.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Blob/ABI43_0_0React-RCTBlob.podspec index 7269954ff75d8..455f133ef314f 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Blob/ABI43_0_0React-RCTBlob.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Blob/ABI43_0_0React-RCTBlob.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTBlob" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Image/ABI43_0_0React-RCTImage.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Image/ABI43_0_0React-RCTImage.podspec index e24f5ea63e20c..10ce7fbed4e00 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Image/ABI43_0_0React-RCTImage.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Image/ABI43_0_0React-RCTImage.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTImage" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/LinkingIOS/ABI43_0_0React-RCTLinking.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/LinkingIOS/ABI43_0_0React-RCTLinking.podspec index 0eda8131190c3..24708fdaff8be 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/LinkingIOS/ABI43_0_0React-RCTLinking.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/LinkingIOS/ABI43_0_0React-RCTLinking.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTLinking" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/NativeAnimation/ABI43_0_0React-RCTAnimation.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/NativeAnimation/ABI43_0_0React-RCTAnimation.podspec index c8679a0a81698..b73bab1e562af 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/NativeAnimation/ABI43_0_0React-RCTAnimation.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/NativeAnimation/ABI43_0_0React-RCTAnimation.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTAnimation" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Network/ABI43_0_0React-RCTNetwork.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Network/ABI43_0_0React-RCTNetwork.podspec index ccee4cf3d3bd5..df82cfe6be53b 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Network/ABI43_0_0React-RCTNetwork.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Network/ABI43_0_0React-RCTNetwork.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTNetwork" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/PushNotificationIOS/ABI43_0_0React-RCTPushNotification.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/PushNotificationIOS/ABI43_0_0React-RCTPushNotification.podspec index 421ce21f67647..98e1a5a123e1a 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/PushNotificationIOS/ABI43_0_0React-RCTPushNotification.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/PushNotificationIOS/ABI43_0_0React-RCTPushNotification.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTPushNotification" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Settings/ABI43_0_0React-RCTSettings.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Settings/ABI43_0_0React-RCTSettings.podspec index 2a4ffe010fa10..280527ea1c600 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Settings/ABI43_0_0React-RCTSettings.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Settings/ABI43_0_0React-RCTSettings.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTSettings" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/TypeSafety/ABI43_0_0RCTTypeSafety.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/TypeSafety/ABI43_0_0RCTTypeSafety.podspec index df3ea56fd6cd4..7cbfb9b90ab49 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/TypeSafety/ABI43_0_0RCTTypeSafety.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/TypeSafety/ABI43_0_0RCTTypeSafety.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0RCTTypeSafety" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Vibration/ABI43_0_0React-RCTVibration.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Vibration/ABI43_0_0React-RCTVibration.podspec index 1378973295f40..a80085ad1b2ab 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Vibration/ABI43_0_0React-RCTVibration.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/Libraries/Vibration/ABI43_0_0React-RCTVibration.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-RCTVibration" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/ABI43_0_0React-RCTFabric.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/ABI43_0_0React-RCTFabric.podspec index 2dcb66391f3a4..789cce33b435e 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/ABI43_0_0React-RCTFabric.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/ABI43_0_0React-RCTFabric.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1' folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -31,8 +31,8 @@ Pod::Spec.new do |s| s.header_dir = "ABI43_0_0React" s.framework = "JavaScriptCore" s.library = "stdc++" - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" } - s.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" } + s.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED" + " " + folly_flags } s.dependency "ABI43_0_0React-Core", version diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/CoreModules/ABI43_0_0React-CoreModules.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/CoreModules/ABI43_0_0React-CoreModules.podspec index 748936413b396..a4449319909c4 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/CoreModules/ABI43_0_0React-CoreModules.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/CoreModules/ABI43_0_0React-CoreModules.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0React-CoreModules" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/FBReactNativeSpec/ABI43_0_0FBReactNativeSpec.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/FBReactNativeSpec/ABI43_0_0FBReactNativeSpec.podspec index 0e8f275136bd5..4712673351e82 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/FBReactNativeSpec/ABI43_0_0FBReactNativeSpec.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/FBReactNativeSpec/ABI43_0_0FBReactNativeSpec.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI43_0_0FBReactNativeSpec" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/third-party.xcconfig b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/third-party.xcconfig index ada55f5ebe8ce..683e73b1ebb44 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/React/third-party.xcconfig +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/React/third-party.xcconfig @@ -8,5 +8,5 @@ // LICENSE file in the root directory of this source tree. // -HEADER_SEARCH_PATHS = $(SRCROOT)/../third-party/boost_1_63_0 $(SRCROOT)/../third-party/folly-2020.01.13.00 $(SRCROOT)/../third-party/glog-0.3.5/src +HEADER_SEARCH_PATHS = $(SRCROOT)/../third-party/boost_1_63_0 $(SRCROOT)/../third-party/folly-2021.06.28.00 $(SRCROOT)/../third-party/glog-0.3.5/src OTHER_CFLAGS = -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0React-Fabric.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0React-Fabric.podspec index 556bb470cba05..d21e60c71876f 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0React-Fabric.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0React-Fabric.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' folly_dep_name = 'RCT-Folly/Fabric' boost_compiler_flags = '-Wno-documentation' @@ -76,7 +76,7 @@ Pod::Spec.new do |s| ss.source_files = "react/renderer/core/**/*.{m,mm,cpp,h}" ss.exclude_files = "react/renderer/core/tests" ss.header_dir = "ABI43_0_0react/renderer/core" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end s.subspec "componentregistry" do |ss| diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0ReactCommon.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0ReactCommon.podspec index 4bdddd4bbc6cc..973523f327c48 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0ReactCommon.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/ABI43_0_0ReactCommon.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.source = { :path => "." } s.header_dir = "ABI43_0_0ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI43_0_0React-Core\"", "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" } diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/callinvoker/ABI43_0_0React-callinvoker.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/callinvoker/ABI43_0_0React-callinvoker.podspec index fcb0915cc8f13..85e3ae314f6a7 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/callinvoker/ABI43_0_0React-callinvoker.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/callinvoker/ABI43_0_0React-callinvoker.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/cxxreact/ABI43_0_0React-cxxreact.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/cxxreact/ABI43_0_0React-cxxreact.podspec index 6a7919371fd54..1e7652f905e20 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/cxxreact/ABI43_0_0React-cxxreact.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/cxxreact/ABI43_0_0React-cxxreact.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,10 +27,10 @@ Pod::Spec.new do |s| s.source_files = "*.{cpp,h}" s.exclude_files = "ABI43_0_0SampleCxxModule.*" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI43_0_0cxxreact" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsi/ABI43_0_0React-jsi.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsi/ABI43_0_0React-jsi.podspec index f756ccbf7b021..ac991dbe3f8bc 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsi/ABI43_0_0React-jsi.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsi/ABI43_0_0React-jsi.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,11 +27,11 @@ Pod::Spec.new do |s| s.exclude_files = "**/test/*" s.framework = "JavaScriptCore" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI43_0_0jsi" s.default_subspec = "Default" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsiexecutor/ABI43_0_0React-jsiexecutor.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsiexecutor/ABI43_0_0React-jsiexecutor.podspec index 7885d86e1efab..66202027a5a5b 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsiexecutor/ABI43_0_0React-jsiexecutor.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/jsiexecutor/ABI43_0_0React-jsiexecutor.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -25,7 +25,7 @@ Pod::Spec.new do |s| s.source = { :path => "." } s.source_files = "jsireact/*.{cpp,h}" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI43_0_0jsireact" s.dependency "ABI43_0_0React-cxxreact", version diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI43_0_0React-graphics.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI43_0_0React-graphics.podspec index 3766787bbb046..a6699debe4c50 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI43_0_0React-graphics.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI43_0_0React-graphics.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -30,7 +30,7 @@ Pod::Spec.new do |s| "platform/android", "platform/cxx" s.header_dir = "ABI43_0_0react/renderer/graphics" - s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/../../../\" \"$(PODS_ROOT)/RCT-Folly\"" } + s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_TARGET_SRCROOT)/../../../\" \"$(PODS_ROOT)/RCT-Folly\"" } s.dependency "RCT-Folly/Fabric", folly_version end diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/reactperflogger/ABI43_0_0React-perflogger.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/reactperflogger/ABI43_0_0React-perflogger.podspec index 3bfd84696f1d0..88fe27c65cac1 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/reactperflogger/ABI43_0_0React-perflogger.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/reactperflogger/ABI43_0_0React-perflogger.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI43_0_0React-runtimeexecutor.podspec b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI43_0_0React-runtimeexecutor.podspec index 8ad7e91b54dcb..6f00d8ac3bb8b 100644 --- a/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI43_0_0React-runtimeexecutor.podspec +++ b/ios/versioned-react-native/ABI43_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI43_0_0React-runtimeexecutor.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/ios/versioned-react-native/ABI44_0_0/Expo/ExpoKit/ABI44_0_0ExpoKit.podspec b/ios/versioned-react-native/ABI44_0_0/Expo/ExpoKit/ABI44_0_0ExpoKit.podspec index fc8d68b96921b..dc6fb29d361bb 100644 --- a/ios/versioned-react-native/ABI44_0_0/Expo/ExpoKit/ABI44_0_0ExpoKit.podspec +++ b/ios/versioned-react-native/ABI44_0_0/Expo/ExpoKit/ABI44_0_0ExpoKit.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |s| s.source = { :git => "http://github.com/expo/expo.git" } s.xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++14', - 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", + 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", 'OTHER_CPLUSPLUSFLAGS' => [ "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -32,11 +32,11 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", - "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " + "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/React-Core\" " } s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.xcconfig = { - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/ABI44_0_0React-Core\"", + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/ABI44_0_0React-Core\"", "OTHER_CFLAGS" => "$(inherited)" + " " + folly_flags } diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ABI44_0_0React-Core.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ABI44_0_0React-Core.podspec index 9997ccb7a33ad..095cfbc6d548f 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ABI44_0_0React-Core.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ABI44_0_0React-Core.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' header_subspecs = { @@ -42,7 +42,7 @@ Pod::Spec.new do |s| s.header_dir = "ABI44_0_0React" s.framework = "JavaScriptCore" s.library = "stdc++" - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\"", "DEFINES_MODULE" => "YES" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\"", "DEFINES_MODULE" => "YES" } s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/ABI44_0_0React-Core\""} s.default_subspec = "Default" s.module_map = "ABI44_0_0React-Core.modulemap" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Blob/ABI44_0_0React-RCTBlob.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Blob/ABI44_0_0React-RCTBlob.podspec index c932908e605ad..e47ac33c5603e 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Blob/ABI44_0_0React-RCTBlob.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Blob/ABI44_0_0React-RCTBlob.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTBlob" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Image/ABI44_0_0React-RCTImage.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Image/ABI44_0_0React-RCTImage.podspec index 4fb0a604359d5..a0773787e69b5 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Image/ABI44_0_0React-RCTImage.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Image/ABI44_0_0React-RCTImage.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTImage" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/LinkingIOS/ABI44_0_0React-RCTLinking.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/LinkingIOS/ABI44_0_0React-RCTLinking.podspec index a1ae538c59b5d..583bf41372180 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/LinkingIOS/ABI44_0_0React-RCTLinking.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/LinkingIOS/ABI44_0_0React-RCTLinking.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTLinking" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/NativeAnimation/ABI44_0_0React-RCTAnimation.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/NativeAnimation/ABI44_0_0React-RCTAnimation.podspec index f3de5b71abd4d..5a3a267c889e8 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/NativeAnimation/ABI44_0_0React-RCTAnimation.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/NativeAnimation/ABI44_0_0React-RCTAnimation.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTAnimation" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Network/ABI44_0_0React-RCTNetwork.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Network/ABI44_0_0React-RCTNetwork.podspec index f5f82edad58b8..ba909d46afa4b 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Network/ABI44_0_0React-RCTNetwork.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Network/ABI44_0_0React-RCTNetwork.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTNetwork" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/PushNotificationIOS/ABI44_0_0React-RCTPushNotification.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/PushNotificationIOS/ABI44_0_0React-RCTPushNotification.podspec index cb3d755e1adb8..cb9a130b41fa5 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/PushNotificationIOS/ABI44_0_0React-RCTPushNotification.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/PushNotificationIOS/ABI44_0_0React-RCTPushNotification.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTPushNotification" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Settings/ABI44_0_0React-RCTSettings.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Settings/ABI44_0_0React-RCTSettings.podspec index 41f33f0af530d..1d0b56d5d7587 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Settings/ABI44_0_0React-RCTSettings.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Settings/ABI44_0_0React-RCTSettings.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTSettings" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/TypeSafety/ABI44_0_0RCTTypeSafety.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/TypeSafety/ABI44_0_0RCTTypeSafety.podspec index 08e03c8941992..d1d8b9dd6401c 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/TypeSafety/ABI44_0_0RCTTypeSafety.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/TypeSafety/ABI44_0_0RCTTypeSafety.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0RCTTypeSafety" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Vibration/ABI44_0_0React-RCTVibration.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Vibration/ABI44_0_0React-RCTVibration.podspec index f7030751c4a60..af0b06588445c 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Vibration/ABI44_0_0React-RCTVibration.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/Libraries/Vibration/ABI44_0_0React-RCTVibration.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-RCTVibration" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/ABI44_0_0React-RCTFabric.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/ABI44_0_0React-RCTFabric.podspec index 9e01e1aeded0b..60e713fa5cd06 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/ABI44_0_0React-RCTFabric.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/ABI44_0_0React-RCTFabric.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1' folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -31,8 +31,8 @@ Pod::Spec.new do |s| s.header_dir = "ABI44_0_0React" s.framework = "JavaScriptCore" s.library = "stdc++" - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" } - s.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" } + s.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\"", "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED" + " " + folly_flags } s.dependency "ABI44_0_0React-Core", version diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/CoreModules/ABI44_0_0React-CoreModules.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/CoreModules/ABI44_0_0React-CoreModules.podspec index a9696ccde55ed..a3a5607a3efa8 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/CoreModules/ABI44_0_0React-CoreModules.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/CoreModules/ABI44_0_0React-CoreModules.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0React-CoreModules" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/FBReactNativeSpec/ABI44_0_0FBReactNativeSpec.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/FBReactNativeSpec/ABI44_0_0FBReactNativeSpec.podspec index d894fb7bef5e0..9dd9c569f860c 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/FBReactNativeSpec/ABI44_0_0FBReactNativeSpec.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/FBReactNativeSpec/ABI44_0_0FBReactNativeSpec.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' Pod::Spec.new do |s| s.name = "ABI44_0_0FBReactNativeSpec" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/third-party.xcconfig b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/third-party.xcconfig index ada55f5ebe8ce..683e73b1ebb44 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/React/third-party.xcconfig +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/React/third-party.xcconfig @@ -8,5 +8,5 @@ // LICENSE file in the root directory of this source tree. // -HEADER_SEARCH_PATHS = $(SRCROOT)/../third-party/boost_1_63_0 $(SRCROOT)/../third-party/folly-2020.01.13.00 $(SRCROOT)/../third-party/glog-0.3.5/src +HEADER_SEARCH_PATHS = $(SRCROOT)/../third-party/boost_1_63_0 $(SRCROOT)/../third-party/folly-2021.06.28.00 $(SRCROOT)/../third-party/glog-0.3.5/src OTHER_CFLAGS = -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0React-Fabric.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0React-Fabric.podspec index a109ff9485955..dc9a21a81af75 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0React-Fabric.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0React-Fabric.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' folly_dep_name = 'RCT-Folly/Fabric' boost_compiler_flags = '-Wno-documentation' @@ -76,7 +76,7 @@ Pod::Spec.new do |s| ss.source_files = "react/renderer/core/**/*.{m,mm,cpp,h}" ss.exclude_files = "react/renderer/core/tests" ss.header_dir = "ABI44_0_0react/renderer/core" - ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/RCT-Folly\"" } end s.subspec "componentregistry" do |ss| diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0ReactCommon.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0ReactCommon.podspec index 50479cbd6fd51..3c454ebbcc47d 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0ReactCommon.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/ABI44_0_0ReactCommon.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.source = { :path => "." } s.header_dir = "ABI44_0_0ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI44_0_0React-Core\"", + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/Headers/Private/ABI44_0_0React-Core\"", "USE_HEADERMAP" => "YES", "DEFINES_MODULE" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" } diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/callinvoker/ABI44_0_0React-callinvoker.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/callinvoker/ABI44_0_0React-callinvoker.podspec index c4e8df96395a2..5429888df4a1d 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/callinvoker/ABI44_0_0React-callinvoker.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/callinvoker/ABI44_0_0React-callinvoker.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/cxxreact/ABI44_0_0React-cxxreact.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/cxxreact/ABI44_0_0React-cxxreact.podspec index 9ef186830ee29..27c74893af4ec 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/cxxreact/ABI44_0_0React-cxxreact.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/cxxreact/ABI44_0_0React-cxxreact.podspec @@ -12,7 +12,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,10 +27,10 @@ Pod::Spec.new do |s| s.source_files = "*.{cpp,h}" s.exclude_files = "ABI44_0_0SampleCxxModule.*" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI44_0_0cxxreact" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsi/ABI44_0_0React-jsi.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsi/ABI44_0_0React-jsi.podspec index 3415fe098063b..53b8d0aea1d61 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsi/ABI44_0_0React-jsi.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsi/ABI44_0_0React-jsi.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -27,11 +27,11 @@ Pod::Spec.new do |s| s.exclude_files = "**/test/*" s.framework = "JavaScriptCore" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI44_0_0jsi" s.default_subspec = "Default" - s.dependency "boost-for-react-native", "1.63.0" + s.dependency "boost", "1.76.0" s.dependency "DoubleConversion" s.dependency "RCT-Folly", folly_version s.dependency "glog" diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsiexecutor/ABI44_0_0React-jsiexecutor.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsiexecutor/ABI44_0_0React-jsiexecutor.podspec index a96bb2ba2f7f1..05ae8f38236d6 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsiexecutor/ABI44_0_0React-jsiexecutor.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/jsiexecutor/ABI44_0_0React-jsiexecutor.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -25,7 +25,7 @@ Pod::Spec.new do |s| s.source = { :path => "." } s.source_files = "jsireact/*.{cpp,h}" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags - s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\"" } s.header_dir = "ABI44_0_0jsireact" s.dependency "ABI44_0_0React-cxxreact", version diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI44_0_0React-graphics.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI44_0_0React-graphics.podspec index a869762bc3f76..07a75054dab8c 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI44_0_0React-graphics.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/react/renderer/graphics/ABI44_0_0React-graphics.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| @@ -30,7 +30,7 @@ Pod::Spec.new do |s| "platform/android", "platform/cxx" s.header_dir = "ABI44_0_0react/renderer/graphics" - s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_TARGET_SRCROOT)/../../../\" \"$(PODS_ROOT)/RCT-Folly\"" } + s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_TARGET_SRCROOT)/../../../\" \"$(PODS_ROOT)/RCT-Folly\"" } s.dependency "RCT-Folly/Fabric", folly_version end diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/reactperflogger/ABI44_0_0React-perflogger.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/reactperflogger/ABI44_0_0React-perflogger.podspec index de21475796bf9..50e0f90a00874 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/reactperflogger/ABI44_0_0React-perflogger.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/reactperflogger/ABI44_0_0React-perflogger.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI44_0_0React-runtimeexecutor.podspec b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI44_0_0React-runtimeexecutor.podspec index bc9e90cbf67b2..78d886dce5f97 100644 --- a/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI44_0_0React-runtimeexecutor.podspec +++ b/ios/versioned-react-native/ABI44_0_0/ReactNative/ReactCommon/runtimeexecutor/ABI44_0_0React-runtimeexecutor.podspec @@ -11,7 +11,7 @@ version = package['version'] folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -folly_version = '2020.01.13.00' +folly_version = '2021.06.28.00-v2' boost_compiler_flags = '-Wno-documentation' Pod::Spec.new do |s| diff --git a/packages/expo/android/build.gradle b/packages/expo/android/build.gradle index 8e92b4503502a..c62fcd4bc16c9 100644 --- a/packages/expo/android/build.gradle +++ b/packages/expo/android/build.gradle @@ -80,13 +80,6 @@ android { } } } - - unitTestVariants.all { - it.mergedFlavor.manifestPlaceholders = [ - // Fix expo-app-auth manifest merger issue for unit test - appAuthRedirectScheme: "test" - ] - } } dependencies { dependencyHandler -> diff --git a/packages/expo/android/src/test/AndroidManifest.xml b/packages/expo/android/src/test/AndroidManifest.xml new file mode 100644 index 0000000000000..410bf428b586f --- /dev/null +++ b/packages/expo/android/src/test/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/react-native-lab/react-native b/react-native-lab/react-native index b8c29af50fb36..94575ee31d104 160000 --- a/react-native-lab/react-native +++ b/react-native-lab/react-native @@ -1 +1 @@ -Subproject commit b8c29af50fb36b6a74244b6cc26c203969fcb3bb +Subproject commit 94575ee31d1041edb76dc16b172954af2d229d14 diff --git a/template-files/ios/ExpoKit-Podfile b/template-files/ios/ExpoKit-Podfile index d7f7ff918024c..8f53529363f2f 100644 --- a/template-files/ios/ExpoKit-Podfile +++ b/template-files/ios/ExpoKit-Podfile @@ -22,13 +22,14 @@ target 'ExpoKitApp' do 'expo-in-app-purchases', 'expo-payments-stripe', 'expo-module-template', - 'expo-image' + 'expo-image', + 'expo-dev-menu', + 'expo-dev-menu-interface', + 'expo-dev-launcher', + 'expo-dev-client' ], ) - # Pods that are not expo modules - pod 'EXRandom', path: '../../../packages/expo-random/ios' - # Install React Native and its dependencies require_relative '../node_modules/react-native/scripts/react_native_pods' use_react_native!(production: true) @@ -43,6 +44,9 @@ target 'ExpoKitApp' do installer.pods_project.main_group.tab_width = '2'; installer.pods_project.main_group.indent_width = '2'; + # Workaround build error for Folly + __apply_Xcode_12_5_M1_post_install_workaround(installer) if installer.pods_project + installer.target_installation_results.pod_target_installation_results .each do |pod_name, target_installation_result| @@ -69,7 +73,7 @@ target 'ExpoKitApp' do target_installation_result.native_target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' end @@ -77,7 +81,7 @@ target 'ExpoKitApp' do # projects which don't reference ExponentCPP. if pod_name.start_with?('React') target_installation_result.native_target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' config.build_settings['HEADER_SEARCH_PATHS'] ||= ['$(inherited)'] end end diff --git a/template-files/ios/ExpoKit-Podfile-versioned b/template-files/ios/ExpoKit-Podfile-versioned index ce2f25931d0fa..d0fc7a839ef57 100644 --- a/template-files/ios/ExpoKit-Podfile-versioned +++ b/template-files/ios/ExpoKit-Podfile-versioned @@ -19,6 +19,9 @@ ${PODFILE_VERSIONED_RN_DEPENDENCIES} installer.pods_project.main_group.tab_width = '2'; installer.pods_project.main_group.indent_width = '2'; + # Workaround build error for Folly + __apply_Xcode_12_5_M1_post_install_workaround(installer) if installer.pods_project + installer.target_installation_results.pod_target_installation_results .each do |pod_name, target_installation_result| ${PODFILE_DETACHED_SERVICE_POSTINSTALL} diff --git a/template-files/ios/ExpoKit.podspec b/template-files/ios/ExpoKit.podspec index 980e292712c93..26200f5102fd4 100644 --- a/template-files/ios/ExpoKit.podspec +++ b/template-files/ios/ExpoKit.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.source = { :git => "http://github.com/expo/expo.git" } s.xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++14', - 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", + 'SYSTEM_HEADER_SEARCH_PATHS' => "\"$(PODS_ROOT\)\/boost\" \"$(PODS_ROOT\)\/RCT-Folly\" \"$(PODS_ROOT)/Headers/Private/React-Core\"", 'OTHER_CPLUSPLUSFLAGS' => [ "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", diff --git a/tools/src/commands/AndroidBuildPackages.ts b/tools/src/commands/AndroidBuildPackages.ts index 44a2651c777d7..f8fee06a384b3 100644 --- a/tools/src/commands/AndroidBuildPackages.ts +++ b/tools/src/commands/AndroidBuildPackages.ts @@ -182,7 +182,7 @@ async function _updateExpoViewAsync(packages: Package[], sdkVersion: string): Pr `api 'com.facebook.react:react-native:${sdkVersion}'` ); await _regexFileAsync( - path.join(ANDROID_DIR, 'ReactAndroid', 'release.gradle'), + path.join(ANDROID_DIR, 'ReactAndroid', 'build.gradle'), /version = '[\d.]+'/, `version = '${sdkVersion}'` ); @@ -239,9 +239,7 @@ async function _updateExpoViewAsync(packages: Package[], sdkVersion: string): Pr for (const pkg of packages) { process.stdout.write(` 🛠 Building ${pkg.name}...`); try { - // TODO: Update to the actual action when we upgrade our react-native fork - const gradleAction = pkg.name === 'ReactAndroid' ? 'uploadArchives' : 'publish'; - await spawnAsync('./gradlew', [`:${pkg.name}:${gradleAction}`], { + await spawnAsync('./gradlew', [`:${pkg.name}:publish`], { cwd: ANDROID_DIR, }); readline.clearLine(process.stdout, 0); diff --git a/tools/src/commands/UpdateReactNative.ts b/tools/src/commands/UpdateReactNative.ts index 0592235769be8..8c6d21463f8fe 100644 --- a/tools/src/commands/UpdateReactNative.ts +++ b/tools/src/commands/UpdateReactNative.ts @@ -49,20 +49,26 @@ async function updateReactAndroidAsync(sdkVersion: string): Promise { logger.info( '📇 Transforming', - chalk.magenta('Application.mk'), + chalk.magenta('ReactAndroid/build.gradle'), 'to make use of', chalk.yellow('NDK_ABI_FILTERS') ); - await transformFileAsync(REACT_APPLICATION_MK_PATH, [ + await transformFileAsync(REACT_ANDROID_GRADLE_PATH, [ { - find: /^APP_ABI := (.*)$/m, - replaceWith: 'APP_ABI := $(if $(NDK_ABI_FILTERS),$(NDK_ABI_FILTERS),$($1))', + find: /^(def reactNativeArchitectures\(\) {)/m, + replaceWith: `$1\n if (System.getenv('NDK_ABI_FILTERS')) { return System.getenv('NDK_ABI_FILTERS'); }`, }, ]); await transformFileAsync(REACT_ANDROID_GRADLE_PATH, [ { find: /^(\s*jsRootDir\s*=\s*)file\(.+\)$/m, - replaceWith: '$1file("$projectDir/../../react-native-lab/react-native/Libraries")', + replaceWith: + '$1file("$projectDir/../../react-native-lab/react-native/Libraries")' + + '\n codegenDir = file("$projectDir/../../react-native-lab/react-native/packages/react-native-codegen")', + }, + { + find: /^(\s*reactRoot\s*=\s*)file\(.+\)$/m, + replaceWith: '$1file("$projectDir/../../react-native-lab/react-native")', }, { find: /^(\s*reactNativeRootDir\s*=\s*)file\(.+\)$/m, diff --git a/tools/src/vendoring/config/expoGoConfig.ts b/tools/src/vendoring/config/expoGoConfig.ts index 025ece1f7caf1..6d98af892be68 100644 --- a/tools/src/vendoring/config/expoGoConfig.ts +++ b/tools/src/vendoring/config/expoGoConfig.ts @@ -59,7 +59,7 @@ const config: VendoringTargetConfig = { ios: { async preReadPodspecHookAsync(podspecPath: string): Promise { let content = await fs.readFile(podspecPath, 'utf-8'); - content = content.replace("reactVersion = '0.66.0'", "reactVersion = '0.64.3'"); + content = content.replace("reactVersion = '0.66.0'", "reactVersion = '0.67.2'"); content = content.replace(/(puts "\[RNReanimated\].*$)/gm, '# $1'); await fs.writeFile(podspecPath, content); return podspecPath; diff --git a/tools/src/vendoring/legacy.ts b/tools/src/vendoring/legacy.ts index 9b22949c988f9..05ebc6eda908c 100644 --- a/tools/src/vendoring/legacy.ts +++ b/tools/src/vendoring/legacy.ts @@ -161,7 +161,7 @@ const ReanimatedModifier: ModuleModifier = async function ( }; const applyRNVersionPatches = async () => { - const rnVersion = '0.64.3'; + const rnVersion = '0.67.2'; const patchVersion = rnVersion.split('.')[1]; const patchSourceDir = path.join(clonedProjectPath, 'android', 'rnVersionPatch', patchVersion); const javaFiles = await glob('**/*.java', {