From 9a02986faed2601b737fe7031ba34f1f4d3bfbfa Mon Sep 17 00:00:00 2001 From: Adam Gleitman Date: Mon, 16 Aug 2021 14:00:00 -0700 Subject: [PATCH] Merge from upstream through 2020-05-28 (#811) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove `docs` from `jscodeshift` Summary: I removed 1 MiB from `jscodeshift` as it was shipping with docs: https://github.com/facebook/jscodeshift/commit/5885662920ed7a3077da22beed4a8f76743dd19c Changelog: [Internal] Reviewed By: yungsters Differential Revision: D21368889 fbshipit-source-id: 452fd4cedcc749d972adbb69df5c95117dd55b15 * iOS: when the bridge has been invalidated, NativeModule lookup should just return nil Summary: There's a corner case where: * The bridge gets invalidated, and native modules are cleaning up themselves (but not done yet) * Something asks for a NativeModule instance - ideally it should just get nil at this point * If TM Manager is invalidating, we get nil correctly, but we continue thru the old NativeModule lookup logic * The latter will fail anyway, and would throw a redbox (RCTLogError). So, if the bridge is invalidated, if TM Manager returns nil, we should just return nil for old NativeModule lookup. The module of interest is RCTImageLoader, which was requested by RCTImageView on deallocation. The problem is RCTImageView got dealloc'ed **after** the bridge has been invalidated, so the lookup would always fail... Bonus: RCTImageView should just keep a weak ref to the RCTImageLoader, so that: * if the imageLoader is still alive on image dealloc, it can still access them (last minute "meaningless" cleanup) * if the imageLoader is gone, then the image deallocation doesn't do anything Changelog: [iOS] [Fixed] - Fix module lookup race condition on bridge invalidation. Reviewed By: p-sun, sammy-SC Differential Revision: D21371845 fbshipit-source-id: 862dc07de18ddbfb90e87e24b8dbd001147ddce4 * Fix invalid type annotations Summary: Our parsers accept these but they are invalid. This fixes them. Changelog: [Internal] Reviewed By: yungsters Differential Revision: D21373812 fbshipit-source-id: 6084757b9f842644fe629ae5e6d85baec611588d * Fixes TextInput shaking when typing Chinese (#28805) Summary: Fixes https://github.com/facebook/react-native/issues/28488. ## Changelog [iOS] [Fixed] - Fixes TextInput shaking when typing Chinese Pull Request resolved: https://github.com/facebook/react-native/pull/28805 Test Plan: Demo see https://github.com/facebook/react-native/issues/28488. Differential Revision: D21376803 Pulled By: shergin fbshipit-source-id: b1fe6cc5f67d42ef98a6c12b8ab9990feac0e2a7 * VirtualizedList: Migrate to React.Context Summary: Migrates `VirtualizedList` off legacy context by creating `VirtualizedListContext`. Changelog: [General][Changed] - Migrated `virtualizedList` legacy context to `React.Context`. Reviewed By: TheSavior Differential Revision: D21370882 fbshipit-source-id: 2fa99ee0bc0e6b747a2d3fe7c66ee402c6b9c5af * VirtualizedList: Remove `PropTypes` Dependency Summary: Removes `PropTypes` as a dependency of `VirtualizedList` by no longer validating the return value of `getItemLayout`. Changelog: [Internal] Reviewed By: TheSavior, cpojer Differential Revision: D21370890 fbshipit-source-id: 966db3557b714987aa91179c7654a5ebf27818ad * Remove `@babel/preset-env` Summary: `babel/preset-env` pulls in a number of unused deps, like `caniuse-lite` (3 MiB) that knows about which browsers support certain features. We do not ship to browsers and always know which version of node we are using, so we don't need to pull this in. I changed `jscodeshift` to optionally depend on `babel/preset-env` instead of always pulling it in. This reduces node_modules by 7 MiB. Changelog: [Internal] Reviewed By: yungsters Differential Revision: D21374475 fbshipit-source-id: 6f55e96e990ec0ca12f17bb3657bfa5429796b93 * Add prepareForReuse to RCTSafeAreaViewComponentView Summary: Changelog: [internal] As part of recycle, we should delete state. This is a common pattern used in other components as well. Reviewed By: shergin Differential Revision: D21348782 fbshipit-source-id: a5dee2f4ccee9b19498db31dab1983d8879dca71 * chore: remove Kotlin version from the default template (#28626) Summary: The default application requires Kotlin version that is not supported by the Gradle plugin (should be at least `1.3.10`). However, instead of upgrading, we should remove it entirely. Here's why. This commit https://github.com/facebook/react-native/commit/29d3dfbd196176af98c9727c82ff2668e697d78e introduced Detox for RNTester Android application. Since the commit doesn't mention Detox for the default application and there are no Detox tests present by default in the default application, I believe that this addition was performed by a mistake. The best way is to remove Kotlin from the default template. This step is described in the Detox documentation and all users that are integrating Detox will be asked to perform it anyway. No need to do it for them. ## Changelog [ANDROID] [INTERNAL] - remove Kotlin from the default template Pull Request resolved: https://github.com/facebook/react-native/pull/28626 Test Plan: Building a brand new project with `master` should work Differential Revision: D21388961 Pulled By: shergin fbshipit-source-id: 92666aa67f92b29f4e7f9c036b332bd058cdd49e * Fix Optimized Differ (was generating extraneous Create mutations) Summary: When we call `.erase` on the TinyMap, it sets the key of the element to 0. When we call `.begin()` later, TinyMap will sometimes, but not always, clean up the underlying Vector. This means that *most* of the time, underlying erased elements will be removed from the Vector; but sometimes erased elements will still be there when iterating over it. This was causing us to generate extra "Create" mutations. To fix this, for now we just check for zeroed-out elements when iterating over the vector. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21389371 fbshipit-source-id: 1e641050987d40a3f3e31499dcb373cfb28ae6f8 * Add warning when scrollRef does not have a scrollTo method Summary: Add a `console.warn()` call when calling `_scrollRef.scrollTo`, because `scrollTo` is not guaranteed to exist on `_scrollRef`. Context: `VirtualizedList` holds `_scrollRef`, which is usually a reference to a `ScrollView` component. However, there are several cases where it holds a `View` or other type of component instead. A custom component can be passed in to `renderScrollComponent`, and then `_scrollRef` will point to that custom component. Additionally, if two VirtualizedLists are nested with the same orientation, `_defaultRenderScrollComponent` will return a View instead of a ScrollView. Due to these possibilities, `_scrollRef` is not guaranteed to have a `scrollTo` method. Changelog: [General] [Added] - Add warning when scrollRef does not have a scrollTo method Reviewed By: JoshuaGross, TheSavior Differential Revision: D21386842 fbshipit-source-id: 01e167e0ae0edea8f29853e8b242ce88a5103b49 * Don't use #import in C++ Code (#28825) Summary: While #import is common in Objective C, it's a vendor specific extension in C++, only supported by GCC/Clang, and only when -pedantic is off. Its use causes build breaks with MSVC. Replace it with the standard #include. ## Changelog [Internal] [Fixed] - Don't use #import in C++ Code Pull Request resolved: https://github.com/facebook/react-native/pull/28825 Test Plan: We've ben running this change within react-native-windows for some time. Differential Revision: D21391233 Pulled By: shergin fbshipit-source-id: c0f94f314c46d6ac24067bbdcd5aaaeec9da283f * Fix unit test compilation on Android Summary: Before, compilation fails with P130281113. After fixing BUCK target, fails with P130281201. After all changes, the tests succeed. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21390996 fbshipit-source-id: c85aa43b3ef7fc9642d226ae706c937b2a5a408d * Support calling LayoutAnimation APIs in Fabric from JS Summary: Call Fabric's LayoutAnimation APIs from JS. Changelog: [Internal] A step towards supporting LayoutAnimations on Fabric Reviewed By: shergin, mdvacca Differential Revision: D21297975 fbshipit-source-id: 6d530b01d8152e7c803a7c0299b918a32fb39dc0 * Differ: fix TinyMap to prevent possible crashes in `find()` and `begin()`, and prevent erased elements from being iterated over Summary: The core issue solved in D21389371 was that erased elements of a TinyMap were being iterated over, because TinyMap has somewhat-complicated logic around cleaning out the underlying vector. In some very marginal cases, vectors were not being cleaned and an iterator pointing at erased elements was being returned. The diff prevents some possible crashes in `begin()` and `find()` while making it much less likely to iterate over erased elements. We also add a unit test to catch the case fixed in D21389371, in particular. We also are keeping the code added in D21389371 (for now) since it's a cheap check, and will be a safeguard until we have rigorous testing around TinyMap. To be clear that logic should noop currently, but will prevent crashes in case guarantees around TinyMap change in the future. Currently there is only one line of code that actually uses the TinyMap iterator, so this should be safe. Reviewed By: shergin Differential Revision: D21392762 fbshipit-source-id: 36dc998958c230fad01af93338974f8889cbcf55 * Allow passing partial contentOffset to ScrollView on Android (#28817) Summary: Since support for contentOffset was added to horizontal ScrollView on android (30cc158a875a0414cf53d4d5155410eea5d5aeea) I'm seeing a crash in my app because of a library. What happens is that it passes a partial object for contentOffset so something like `{x: 1}` which causes a crash on Android. According to the flow types the object should always contain both x and y but I think we should preserve the runtime behaviour and just use 0 like iOS does. ## Changelog [Android] [Fixed] - Allow passing partial contentOffset to ScrollView on Android Pull Request resolved: https://github.com/facebook/react-native/pull/28817 Test Plan: Tested that passing partial object for contentOffset does not crash. Reviewed By: JoshuaGross Differential Revision: D21396319 Pulled By: shergin fbshipit-source-id: 4b52c868e3bfe183ff7f68a76ac34d1abd5e1069 * Pass initial props to WrapperComponent Summary: `renderApplication` receives the root component that we need to render and an optional wrapper component. There are cases where we want to use the initial props passed to the root component in the wrapper component as well (e.g.: to provide a specific context to the root component), so this adds modifies `AppContainer` to accept the initial props and inject them into the wrapper component. Changelog: [General] [Added] - Modified `renderApplication` to forward `initialProps` to `WrapperComponent` Reviewed By: fkgozali Differential Revision: D21347486 fbshipit-source-id: 1c4f702a3875077630de1a44d3ac9ef2c80bc10c * Upgrade to Jest 26 Summary: * Brings performance improvements from 25.5.x * Reduces node_modules by 3 MiB, see https://github.com/facebook/jest/pull/9950/files?short_path=63580dd#diff-63580dd1e7078ce037f10f2fee7553b9 * Breaking changes: https://github.com/facebook/jest/blob/master/CHANGELOG.md Changelog: [Internal] Reviewed By: rubennorte Differential Revision: D21369069 fbshipit-source-id: 81a9d50f8e541293a85ce3957cb962930ca05b11 * Make column match fuzzy Summary: We are currently very strict about breakpoint location matching. This diff allows some fuzz in the column, but not in the line. Changelog: [Internal] Setting Hermes breakpoints no longer requires exact column match Reviewed By: avp Differential Revision: D21343198 fbshipit-source-id: a59786a9d63f9fe1ed576835ed660ba3343affe1 * Fix type of exported Touchables: ComponentType -> AbstractComponent (#28737) Summary: Fixes https://github.com/facebook/react-native/issues/28726 When importing TouchableOpacity, it is treated as any by Flow. Replacing ComponentType with AbstractComponent works. The [Flow documentation](https://flow.org/en/docs/react/types/#toc-react-componenttype) says the following about ComponentType: > Note: In 0.89.0+, React.ComponentType is an alias for React.AbstractComponent, which represents a component with config type Config and any instance type. So I'm thinking that since the instance type is treated as any with ComponentType, Flow treats TouchableOpacity as any as well. ## Changelog [General] [Fixed] - Fix Touchable{Opacity,Bounce,Highlight} being exported as `any` (Flow) Pull Request resolved: https://github.com/facebook/react-native/pull/28737 Test Plan: I have done the same changes to react-native in my project's node_modules and seen that the components TouchableOpacity went from any to AbstractComponent with some props. Now I have a bunch of errors because I'm sending in wrong props to some touchables, which is good! Reviewed By: cpojer Differential Revision: D21362601 Pulled By: TheSavior fbshipit-source-id: 5b98cc79eaef034eccdb7f47242f9f44be2ef2b8 * Import folly and adjust whitespace to match old codegen Summary: Import folly to handle optionals (`folly::Optional<__type__>`) Sort modules and indent generated code to match output from the old codegen. While not strictly necessary as these are generated files that should not be edited by hand, I found that matching the old codegen in this regard made it less of a chore when it came to comparing the output of both codebases. Changelog: [Internal] Reviewed By: RSNara Differential Revision: D21395231 fbshipit-source-id: 289d617d7a2d93724456c80afea57a49c108cb9b * Update native module specs Summary: As titled. Reviewed By: fkgozali Differential Revision: D21417307 fbshipit-source-id: 7c6b0179f9f1a5108da241d181a24f707a083deb * Smoother scrolling in ScrollView, HorizontalScrollView Summary: Android ScrollView/HorizontalScrollView `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 pages could be considered part of one animation, causing some page animations to be animated very rapidly - looking like they're not animated at all. We use a custom animation to perform `smoothScrollTo` to improve the UX. This resolves a longstanding issue in non-Fabric RN, as well as Fabric, since this code is shared between the platforms. Changelog: [Update] Android ScrollView/HorizontalScrollView scrolls using custom animations instead of default Android `smoothScrollTo` implementation, leading to smoother scrolls for paginated ScrollViews Reviewed By: mdvacca Differential Revision: D21416520 fbshipit-source-id: 6ebe63cb054a98336b6e81253d35623fe5522f89 * Cleanup unused dependencies Reviewed By: kassens Differential Revision: D21281288 fbshipit-source-id: cf566ad0628dc179b3753f2f25a11637c33dee24 * iOS: Animated image should animate at the same speed regardless of framerate Summary: In iOS 11, [CADisplayLink](https://developer.apple.com/documentation/quartzcore/cadisplaylink)'s frameInterval was deprecated in favor of preferredFramesPerSecond, but these two properties have different underlying assumptions. - set frameInterval to 2 for 30fps - set preferredFramesPerSecond to 30 for 30fps. When you use preferredFramesPerSecond, assume frameInterval is 1. This fix ensures gifs in component will animate at same speed regardless of framerate. Reviewed By: shergin Differential Revision: D21414014 fbshipit-source-id: 40ab23bab1990cf65d2802830b6835f350999537 * LogBox - Always display the first fatal error Summary: This diff fixes an off-by-one error probably caused by my font ligatures where when exactly two exceptions are thrown at the same time we would show the second exception instead of the first. If three or more were thrown, we would show the second. I also fixed some tests that had the wrong descriptions and wrong behavior enforced. Changelog: [Internal] Reviewed By: cpojer Differential Revision: D21413186 fbshipit-source-id: 8e2940c89251dc042b10c6a2a2186089b6e7b53d * Rename error titles Summary: Based on feedback we're updating these titles to be more clear for their source. Changelog: [Internal] Reviewed By: cpojer Differential Revision: D21413486 fbshipit-source-id: c144e7f759a4ff263b7ec80fa643eeb8ffac741b * Moved some NativeModule JS specs to OSS Summary: For some reason the specs were internal, but the native impl is still in github. So let's move these to github for consistency. Changelog: [Internal] Reviewed By: hramos Differential Revision: D21419934 fbshipit-source-id: f2c4486edca43c4348f3a3c6ce98f76a322bab0b * Deploy Flow v0.124.0 to xplat/js Summary: Changelog: [Internal] allow-large-files Reviewed By: samwgoldman, cpojer Differential Revision: D21413059 fbshipit-source-id: f3d111b40bfb88c182eab022925f7ae2dc47bc6b * RN: Workaround Fabric + Virtual Text Press Bug Summary: Workaround for a bug with Fabric when pressing on virtual text. Changelog: [Internal] (Note: this ignores all push blocking failures!) Reviewed By: JoshuaGross Differential Revision: D21432793 fbshipit-source-id: fe20eeadd5365707fb71edae7a76d374e26b4c86 * Fabric: Backward-compatible behaviour of `measureInWindow` and `measure` Summary: Before this change, in case of incorrect measurements, Fabric's implementation of `measure` and `measureInWindow` incorrectly returned negative height and width. Now it returns zeros (as classic React Native does). Fabric: This does not fix `measureLayout` called for virtual nodes. This is not so trivially to fix and it will be done separately. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross, yungsters, mdvacca Differential Revision: D21433239 fbshipit-source-id: fbaf5ee35c690506822c634daac4426542c2cdcf * Upgrade to Yargs 15 Summary: Only breaking changes appear to be dropped Node 6 support: https://github.com/yargs/yargs/blob/master/CHANGELOG.md. Deduplicates quite a few copies of Yargs, yay! Changelog: [Internal] (Note: this ignores all push blocking failures!) Reviewed By: motiz88 Differential Revision: D21426137 fbshipit-source-id: b091e29ac2d9464d6ce9a716a99f7ae156a91a01 * Ez Extend logging of Fabric Summary: Quick diff to log of content of UpdateState mount item. This is useful for debugging. Note this will ONLY be logged when the constant FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT is set to true changelog: [Internal][Android] internal log for fabric android Reviewed By: JoshuaGross Differential Revision: D21428345 fbshipit-source-id: d000eb6dbdd39d15935fa2102072790e17372682 * Fix measureLayout function for Virtual Nodes Summary: This diff fixes a NullPointerException thrown when calling measureLayout function on a virtual node. changelog: [Android] Fix measureLayout function for VirtualTexts Reviewed By: JoshuaGross Differential Revision: D21435030 fbshipit-source-id: aba6d81f333464e49d2d769b111842e7ae8ce769 * Update cocoapods (#28833) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/28833 The checked-in RNTester podfile was generated using Cocoapods version 1.9.1. This doesn't match the version currently used in CircleCI, which is 1.8.4. In order to update the offline mirrors and land them without breaking CircleCI, it seems we need to switch back to 1.8.4. This diff updates the podfile back to 1.8.4 and updates the offline mirrors. Reviewed By: fkgozali Differential Revision: D21392989 fbshipit-source-id: b14aa6e2798175534e9416410ba9d6877fb718c0 * Fabric: Introducing `RunLoopObserver` Summary: `RunLoopObserver` is one of the core interfaces that bridge intrinsically platform-specific functionality to cross-platform React Native core. `RunLoopObserver` allows subscribing for notifications about changes in a run loop life cycle. Primarily it supposed to be used for observing UI (aka main) and JavaScript execution thread/run-loop. Having a `RunLoopObserver` implemented in a platform-specific manner allows building these components in a cross-platform manner: * Sync and async UI event delivery pipeline; * Timing for some animation engine; * Timers (probably additional features are required). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21341997 fbshipit-source-id: 7ef61fb51f550dd0f2e89c64af657e0f0de029aa * Fabric: Cross-platform implementation of `SynchronousEventBeat` and `AsynchronousEventBeat` Summary: `SynchronousEventBeat` and `AsynchronousEventBeat` are a cross-platform re-implementation of run loop related parts of `MainRunLoopEventBeat` and `RuntimeEventBeat` (iOS specific classes for now). In the future, they will replace iOS- and Android-specifc event beat classes. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21341996 fbshipit-source-id: 8eda9a5df537cd666b7728e32212a8bb5ddb3ab7 * third-party | Move glog from xplat/third-party to third-party and create temporary redirect. Summary: Move and create an empty rule that redirects as well, to handle //arvr rules Need to do this way, since ovrsource sync rules are in different repo. allow_many_files allow-large-files Steps: - [X] Move glog from xplat/third-party to /third-party - [ ] Update references in ovrsource to translate to //third-party instead of //xplat/third-party - [ ] Get rid of temporary rule - [ ] Update fbsource/third-party/glog to 0.3.5 (what we have in ovrsource) Changelog: [Internal] Update reference for glog from xplat/third-party to /third-party. Reviewed By: yfeldblum Differential Revision: D21363584 fbshipit-source-id: c1ffe2dd615077170b03d98dcfb77121537793c9 * Fix Animated type Summary: - Fixed typing of Animated and fixed the callsites Changelog: [Internal] Reviewed By: kacieb Differential Revision: D21311870 fbshipit-source-id: 386fb496ab00ef7917273dc3eb65e1ed76a8dd33 * Add virtual destructor to JSError Summary: We consume Hermes through multiple .so's, which means we have multiple (weak) typeinfo definitions of facebook::jsi::JSError. Previously we were using gnustl, which would strcmp typeinfo to decide whether a certain exception handler applies, which meant this didn't cause any major issues. However since this is deprecated, we recently switched to libc++, which does not have this by behaviour (or it does, but behind a flag I'm not sure how to enable). This causes any JS exceptions to fall through from our exception handlers and fatal the app. This problem is actually documented in the common Android NDK problems page: https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#rtti_exceptions-not-working-across-library-boundaries The suggested solution is to ensure that any exception types have a key function defined (a non-pure, out-of-line virtual function). The simplest one to add is a virtual destructor. This makes the object file that holds the implementation of the destructor export a non-weak typeinfo definition which will at load time override the other weak versions. I'm not sure why we're the first to hit this. RN's JSIExecutor doesn't explicitly reference JSError which probably helps (https://github.com/facebook/react-native/blob/master/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L256-L258) and they also don't use unguarded callbacks like we do. Changelog: [Internal] Reviewed By: mhorowitz Differential Revision: D21426524 fbshipit-source-id: 474284ada1ca2810045dc4402c420879447f9308 * Handle initialProps as optional in renderApplication Summary: Pass a default empty object to `AppContainer` if no `initialProps` were passed to `renderApplication`. This prevents issues on Android, where we do not pass a default empty `initialProps` from native, as we do on iOS. Changelog: [General] [Fixed] - Handle nullish `initialProps` correctly in `renderApplication` Reviewed By: motiz88 Differential Revision: D21448692 fbshipit-source-id: 9630bdc2414532999abf3bf9da25047f0482fcab * Enable with CocoaPods `:configuration` (#28796) Summary: ~~⚠️ Depends on https://github.com/facebook/flipper/pull/1086 and a new Flipper release.~~ Fixes https://github.com/facebook/react-native/commit/17f025bc26da13da795845a3f7daee65563420c0#commitcomment-38831234 Currently user’s are being told to add a definition of the `FB_SONARKIT_ENABLED` macro and examples, including those in stock React Native templates, set this for the user by making use of a `post_install` hook in the user’s `Podfile`. This leads to confusion, fragile code [when a user’s project dir structure deviates from vanilla], and is ultimately not necessary as CocoaPods already has dedicated mechanisms to: * specify build settings (through the `xcconfig` property); * and selectively include certain pods only in certain build configurations (e.g. debug). ## Changelog [iOS] [Changed] - Entirely control Flipper being enabled through inclusion in Podfile and optionally limiting to certain build configurations using the `:configuration` directive. Pull Request resolved: https://github.com/facebook/react-native/pull/28796 Test Plan: Tested using the changes of https://github.com/facebook/flipper/pull/1086 in a new app that uses RN `master`. Reviewed By: priteshrnandgaonkar Differential Revision: D21449754 Pulled By: passy fbshipit-source-id: 9ff7c7f4ffc32b364b1edd82b94e0b80c3997625 * Support excluding multiple platforms. Summary: Currently the schema only allows to exclude a single platform (iOS OR Android). There are cases where we need to exclude multiple. This change converts the previous `excludePlatform` string property into an `excludePlatforms` array. Changelog: [Internal][Changed] - Added support to exclude multiple platforms in Codegen. Reviewed By: sammy-SC Differential Revision: D21426950 fbshipit-source-id: eff36ffa207109274794b4b300bf6313f8286161 * Extend ParagraphAttribute to store the includeFontPadding prop Summary: This diff extends the ParagraphAttribute class to store the value of the includeFontPadding prop. Note that this is an Android only prop, I'm not creating android blocks to improve "cleanliness" of the code. changelog: [Internal][Fabric] Internal change in Fabric to support Text.includeFontPadding prop in fabric Reviewed By: shergin Differential Revision: D21446738 fbshipit-source-id: 0543e86aa18ce10f7a56bbaafe111cce0179ea86 * Extend Text measurement to support includeFontPadding prop Summary: This diff exposes the Text.includeFontPadding prop to java, then it uses the prop to calculate the height of Text components correctly. changelog: [Internal][Fabric] Internal change in Fabric to support Text.includeFontPadding prop in fabric Reviewed By: shergin Differential Revision: D21446737 fbshipit-source-id: efe73fb6b0d402c3275ac8c012fa8fa06b743bdd * Small refactor on text measure method Summary: Quick refactor of TextLayoutManager class changelog: [Internal] Reviewed By: shergin Differential Revision: D21446736 fbshipit-source-id: a32bdf534b167e128c8c0054cf6a126131fa740a * Handle optional return types/values Summary: Use folly to wrap optional return types and values as needed. Changelog: [Internal] Reviewed By: RSNara Differential Revision: D21395439 fbshipit-source-id: a0e84e20717887e79a8565332a11fef42ebd3487 * Avoid redefining id when a property is named 'id' Summary: Handle properties named 'id' as a special case. An example of a native module that ran afoul of this is `ExceptionsManager`. Observe how the ExceptionsManager spec at `Libraries/Core/NativeExceptionsManager.js` defines the ExceptionData type as containing an `id` property: ``` export type ExceptionData = { message: string, originalMessage: ?string, name: ?string, componentStack: ?string, stack: Array, id: number, isFatal: boolean, // flowlint-next-line unclear-type:off extraData?: Object, ... }; ``` Prior to this change, the generated code would redefine id in the SpecReportExceptionData struct... ``` namespace JS { namespace NativeExceptionsManager { struct SpecReportExceptionData { // ...redacted... double id() const; <--- // ...redacted... SpecReportExceptionData(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } ``` ...which would result in a build time error: ``` inline double JS::NativeExceptionsManager::SpecReportExceptionData::id() const { id const p = _v[@"id"]; ^--- build time error here return RCTBridgingToDouble(p); } ``` Comparing the above example with the currently checked in `FBReactNativeSpec.h`, I see the expected output should be: ``` namespace JS { namespace NativeExceptionsManager { struct SpecReportExceptionData { // ...redacted... double id_() const; // ...redacted... SpecReportExceptionData(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } ``` ...and... ``` inline double JS::NativeExceptionsManager::SpecReportExceptionData::id_() const { id const p = _v[@"id"]; return RCTBridgingToDouble(p); } ``` Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D21395463 fbshipit-source-id: e412648013ff9f70ebd294b6f5f81f1faccb4604 * Eager initialize Fabric Android classes Summary: This diff eager initializes Fabric Android classes. This should help load all the Fabric classes at Bridge load time. changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21460507 fbshipit-source-id: 4b8d5c4e2d19e3a7eb3077027071e64ff16f1cbd * Remove Hermes.setPauseOnLoad message Summary: This removes the Hermes.setPauseOnLoad. It will be replaced by the more standard Debugger.setInstrumentationBreakpoint's "beforeScriptExecution" event. ChangeLog: [Internal] Remove Hermes.setPauseOnLoad message (to be replaced) Reviewed By: mhorowitz Differential Revision: D21418219 fbshipit-source-id: 93c53801c23487f9336b322c2bd737663ec21b97 * Add support for Debugger.setInstrumentationBreakpoint Summary: This diff adds support for the "beforeScriptWithSourceMapExecution" instrumentation breakpoint via "Debugger.setInstrumentationBreakpoint". CDP describes it as a breakpoint, but we just set a flag in the inspector. A fake breakpoint ID is synthesized for optional removal later. Changelog: [Internal] Add Debugger.setInstrumentationBreakpoint to Hermes Inspector Reviewed By: mhorowitz Differential Revision: D21418218 fbshipit-source-id: 90fa49c0954980993815322d3a7effee416ed5db * label react-native-github targets Summary: For internal code attribution. Changelog: [Internal] Reviewed By: zlern2k Differential Revision: D21468924 fbshipit-source-id: 59cd2a52e0ae46bedbf54816820a5f40b684da8b * Add script to generate native modules specs with react-native-codegen Summary: Adds a script that uses `react-native-codegen` to generate FBReactNativeSpec. The generated output should not be considered ready for production use at this time. The goal of adding this script at this time is to demonstrate the current status of native modules specs code generation in open source. For example, the generated output may be used in RNTester, with some modifications due to some naming differences in react-native-codegen's output when compared to the FBReactNativeSpec files generated by the old codegen. Usage: ``` ./scripts/generate-native-modules-specs.sh ./codegen-out ``` Changelog: [Internal] Reviewed By: TheSavior Differential Revision: D21471004 fbshipit-source-id: 5ff3c57807d9ba2c91dc7fe32d227d218732b059 * Update .gitignore (#28789) Summary: When you profile your heap and memory allocations with Memory Profiler, files with *.hprof extension are created in /android folder that has big sizes (up to 600 MB for each). These files may be needed to add to gitignore. ## Changelog [Android] [Added] - Add *.hprof files to gitignore Pull Request resolved: https://github.com/facebook/react-native/pull/28789 Differential Revision: D21432927 Pulled By: hramos fbshipit-source-id: a20f12645de5ca0874c9130094e2f97fe16b2203 * Codegen: Add prepublish script to build Flow files (#28827) Summary: *This is a follow-up to https://github.com/facebook/react-native/issues/28645, redone using a build script based off of Metro's build script instead of using `flow-remove-types` and `flow-copy-source`.* This pull request adds a build step to `react-native-codegen` that builds the Flow-annotated JS files so that users of the NPM module `react-native-codegen` do not need to use require hooks to be able to import it. A new build script, `scripts/build.js` is added that builds every JS file in `src/` into a `lib/` folder, and also copies over the original Flow annotated files to `lib/` with a `.js.flow` extension, so users of `react-native-codegen` can still typecheck against it using Flow. The shell scripts in `src` are also copied over. It is based off of the [build script from Metro](https://github.com/facebook/metro/blob/00867816eb9b2f69c8af9cebb523e9e4d280671a/scripts/build.js) ## Changelog [General] [Added] - Codegen: Add prepublish script to build Flow files Pull Request resolved: https://github.com/facebook/react-native/pull/28827 Test Plan: I am able to make use of the Codegen scripts without needing to use the `flow-node` CLI or the `flow-remove-types/register` require hook. Reviewed By: cpojer Differential Revision: D21412173 Pulled By: hramos fbshipit-source-id: 26ae67cdd04652ca4700a069a234a25558773cb1 * Remove RCTLogError from RCTScrollViewManager.calculateChildFrames Summary: Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21483553 fbshipit-source-id: 0c681979a4988c36cfa6f05aa5bca896590f9e3d * Enable animations in bridgeless mode on iOS Reviewed By: ejanzer Differential Revision: D21465166 fbshipit-source-id: b34e8e97330b897e20d9a4b05dba1826df569e16 * Expose RuntimeExecutor on CatalystInstance (#28851) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/28851 This diff creates a RuntimeExecutor that uses the bridge and exposes it on CatalystInstanceImpl. Changelog: [Internal] Reviewed By: mdvacca, RSNara Differential Revision: D21051949 fbshipit-source-id: b3977fc14fa19089f33e297d29cedba0d067526d * Inject ImagePipeline into FrescoModule Summary: This diff refactors the FrescoModule in order to receive an ImagePipeline as a parameter. This is necessary to ensure the same ImagePipeline is used by every RN module changelog: [Internal][Android] Reviewed By: JoshuaGross Differential Revision: D21428346 fbshipit-source-id: 70a6cc57c8585fe74b6d0b0d1fd86c539974ec23 * Fabric: Calling JSVM GC on memory pressure event on iOS Summary: This change is especially important for Fabric when a lot of objects (mostly `ShadowNode`s) have shared ownership. Without this change, JSVM could not know that bunch of natively allocated objects should be deallocated. Changelog: [Internal] Fabric-specific internal change. Reviewed By: dulinriley Differential Revision: D21484773 fbshipit-source-id: 46e32de0f108082e60df346884c9287023156149 * TextInput: Default `blurOnSubmit` in JS Summary: Consolidates the logic for the default value of `blurOnSubmit` on `TextInput` in the JavaScript component. This only materially impacts Fabric. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21491482 fbshipit-source-id: 16d8aba32e7d0321a4583e87e03405ea587e35d4 * Fix taps not working after scrolling / scroll position not being updated in C++ Summary: Problem: ScrollView offset was not being reported to the C++ ScrollView side of Fabric. This results in taps not working correctly, for example if you tap a button inside scroll view after you scrolled, the tap might not trigger anything. The root cause of this is our implementation of detecting whether scroll view has stopped scrolling. To make this more robust, I now require that multiple "frames" have not scrolled because it's easy to trigger race conditions by scrolling very fast. We also explicitly call `updateStateOnScroll` in a couple more places. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21496396 fbshipit-source-id: 2e565dd2fc4fc1ce582daa8a449c520e7cb19be0 * Fabric: Changes in UIManager::getNewestCloneOfShadowNode to make it safer Summary: There is no need to use a raw pointer to a shared pointer as a return value of `UIManager::getNewestCloneOfShadowNode`. If it would be a very hot path, we could you a raw pointer to the node instead but it's not a hot path. Prooving that using a raw pointer here is safe is quite complex (and I don't know if it's safe or not). So, I changed it to just a shared pointer. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D21464833 fbshipit-source-id: 813a3c9fca0147afb322db6855a0ce8fd2d47909 * Fabric: Making `UIManager::getRelativeLayoutMetrics` safer Summary: In this method we have to maintain a retaining pointer to the node in order to access it as a raw pointer. See also a comment in the codee. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D21464834 fbshipit-source-id: 69f6894009509e5feca291fab12c019208933816 * Fabric: Making `UIManager::getNewestCloneOfShadowNode` even more safer Summary: We cannot use a raw pointer here because the tree might progress right after we call to `shadowTree.tryCommit` and we will get a dangling pointer as a result. Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D21464832 fbshipit-source-id: 3998b39d29db672da54d4adcb6be55cd3d44d862 * Fabric: Checks for corner cases in UIManager::getNewestCloneOfShadowNode Summary: We should check for corner cases. Another interesting detail is behavior. Now (and previously) we return nullptr if case we could not find the most recent node. But maybe we should return the given node instead? Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D21480874 fbshipit-source-id: bb943c4508ec025316a3665d652e4b5e06dd795c * OSS: fixed incorrect license headers on some files Summary: https://github.com/facebook/react-native/commit/de667fffa4589766b5b4cb3dcf148c7f10267a5d#commitcomment-39068402 had incorrect license headers. This fixed them. Changelog: [General] [Fixed] - fixed license headers on some files Reviewed By: cpojer Differential Revision: D21496796 fbshipit-source-id: f9d6b0cf4af0ecf6caacbae2396d73efbc4975fe * Fabric: Fixed incorrect name of the prop value `justifyContent=space-between` Summary: The name of the value was incorrect. Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D21496651 fbshipit-source-id: 464c98436bb8d5f2b6275b7eab1c32d187e2b23c * Fabric: Initializing EventBeat::OwnerBox with non-null value ahead of time Summary: We use `EventBeat::OwnerBox` in the `EventBeat` infra to represent weak ownership of classes that contain that, so those classes can ensure own life-time at some critical points. Before this diff, we stored a pointer to EventDispatcher right after we create it. However, some components that we build require to have a valid pointer from the beginning (even before the EventDispatcher is created). To satisfy this requirement, we create a dummy pointer first and use it as an owner, then we merge (share control block) the pointer to an EventDispatcher into that. It works because the owner pointer never gets dereferenced (it's `void *`), so it does not matter which object it represents while it represents correct ownership. In the future, this approach will allow us to remove the concept of `OwnerBox` completely and use just `std::weak_ptr` instead. Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D21441109 fbshipit-source-id: 7457c77bd31cd577f38a26e28e27eb7e33b6ad24 * Fabric: Using `PlatformRunLoopObserver` instead of `MainRunLoopEventBeat` and `RuntimeEventBeat` on iOS (gated) Summary: This diff implements iOS-specific `PlatformRunLoopObserver` (and `MainRunLoopObserver`) that then being glued together with `SynchronousEventBeat` replaces `MainRunLoopEventBeat`, and then same thing glued with `AsynchronousEventBeat` replaces `RuntimeEventBeat`. So, instead of two platform-specific classes we had on iOS (for that needs), now we have only one (that can be reused for a more broad variety of applications). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21341998 fbshipit-source-id: fafde4e678770f7fcf9c1ff87acc02812a37e708 * Fix skewX/skewY/perspective/matrix on iOS (#28863) Summary: See discussion on https://github.com/facebook/react-native/commit/c68195929b3d7f7cc1370e36f3e22b4cbd2d2707#commitcomment-38965326 This PR fixes skewX/skewY/perspective/matrix on iOS as seen on this video: https://youtu.be/LK9iOKk62nw?t=115 ## Changelog [iOS] [Fixed] Bug with skewX/skewY/perspective/matrix transforms. Pull Request resolved: https://github.com/facebook/react-native/pull/28863 Test Plan: Try that the following transform as been fixed on iOS ```tsx ``` Differential Revision: D21493022 Pulled By: shergin fbshipit-source-id: 4bf3550941e8acd8fdb87fe1143b21639c95b059 * Fix skewX on Android and in the JS decomposition (#28862) Summary: This issue fixes https://github.com/facebook/react-native/issues/27649. By using 2d decomposition that transforms a skewX into a rotate/scale/rotate, the skewX issue on Android was still there which made me suspect that the issue came from the decomposition algorithm. Then I noticed that the bug existed in the JavaScript decomposition as well which led me to a fix on the JS and therefore on the Android side most likely. ## Changelog [Android] [Fixed] skewX transforms Pull Request resolved: https://github.com/facebook/react-native/pull/28862 Test Plan: Check that skewX works on Android. On JS, making sure that processTransform() doesn't skip, you can try the following sequence: ```tsx const matrix = processTransform([{ skewX: `${Math.PI / 3}rad` }]); const result = MatrixMath.decomposeMatrix(matrix); console.log({ result }); ``` Differential Revision: D21493021 Pulled By: shergin fbshipit-source-id: 89f7aca5fbfd0f0f8c6f90a26bd76bf8550acaa5 * implemented showSoftInputOnFocus for iOS (#28834) Summary: `showSoftInputOnFocus` was added in https://github.com/facebook/react-native/issues/25028, but it was only added for Android. There was a lot of discussion on the original issue being addressed (https://github.com/facebook/react-native/issues/14045), that there is a need for this on iOS as well. The issue with iOS was brought up again on https://github.com/facebook/react-native/issues/27243. On a related note, when searching this repo's issues for `showSoftInputOnFocus`, it appears that there are several closed issues that claim that the Android implementation doesn't work (https://github.com/facebook/react-native/issues/25685, https://github.com/facebook/react-native/issues/25687, https://github.com/facebook/react-native/issues/26643). So perhaps the Android implementation needs to be looked at as well (I myself have not gotten around to confirming whether it works or not) ## Changelog [iOS] [Added] - Add showSoftInputOnFocus to TextInput Pull Request resolved: https://github.com/facebook/react-native/pull/28834 Test Plan: You'd use this just like you would in the Android implementation: ```jsx ``` ## GIFs ### Before change ![May-04-2020 20-52-49](https://user-images.githubusercontent.com/4932784/81034028-9d89cf80-8e4a-11ea-906c-64f62504f80c.gif) ### After change ![May-04-2020 20-54-27](https://user-images.githubusercontent.com/4932784/81034035-a11d5680-8e4a-11ea-918e-119a1c9e2a19.gif) Differential Revision: D21418763 Pulled By: shergin fbshipit-source-id: 561e72fc2cf16b30446132f6b96b8aa2b4a92daf * Fabric: Support for sync `RuntimeExecutor` in `executeSynchronouslyOnSameThread_CAN_DEADLOCK` Summary: The approach is quite simple: we check the thread id, and if it matches the caller thread id, we unlock mutexes which lead to the normal uninterrupted execution flow. Changelog: [Internal] Fabric-specific internal change. Reviewed By: RSNara Differential Revision: D21328313 fbshipit-source-id: 4290b8a0357dbad3563d7da9464c03ecce5ded7c * Use plugin architecture for Paragraph component Summary: Leverage plugin infra for `Paragraph` Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21404704 fbshipit-source-id: 7565014f1d88c4a0a47e0f10fdb3656b8276b1d0 * Use plugins for TextInput component Summary: Leverage plugin infra for `TextInput` Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21407584 fbshipit-source-id: 48b1a69aa834ab956724e6617197da57ccf99aa7 * Use plugins for View component Summary: Leverage plugin infra for `View` Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21407611 fbshipit-source-id: d72d1e75bbd9d06c79544437bdd88f890086c587 * Move SegmentedControl components to separate file and make them function based Summary: Changelog: [Internal] Separate SegmentControl examples into separate file and make them functional based Reviewed By: mdvacca Differential Revision: D21475596 fbshipit-source-id: 19165232f2a8edefa66f122944621f93265ff704 * Add comments to a test case in LayoutableShadowNodeTest Summary: Changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21500371 fbshipit-source-id: daec07f82f43aae9cdc31f67599e30873ca108b9 * xplat code ownership Summary: For internal code attribution. Changelog: [Internal] Reviewed By: zlern2k Differential Revision: D21496969 fbshipit-source-id: 9fa27a758df19c16fd2087ce964132eaa70dc91f * Always run the momentum scroll Runnable so scroll position is updated on Fabric Summary: This early return is a very minor perf optimization, and it complicates things on Fabric: if scroll perf logging is disabled, the scroll position in C++ (State) doesn't get updated for the scrollview. Just remove it. Always run the Runnable, no matter what. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D21503695 fbshipit-source-id: 13dfe232d692ff544bff725a2344a66b572f5444 * Use UIManagerHelper to access EventDispatcher in ReactViewManager Summary: ReactViewManager uses the bridge (CatalystInstance) to access the UIManagerModule in its onClick method. This doesn't work in bridgeless mode, so I'm replacing this callsite with the new API, which uses UIManagerHelper + the reactTag to look up the appropriate UIManager (Paper or Fabric), and get the EventDispatcher from that. Changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21510243 fbshipit-source-id: c2c6111e73c49ca6bf873819db8ece71c66417e4 * Mention using bundler to install cocoapods in RNTester README (#28873) Summary: This makes sure the proper version of cocoapods gets used, this will avoid noise in diffs if someone ends up updating pods with a different version. ## Changelog [Internal] [Changed] - Mention using bundler to install cocoapods in RNTester README Pull Request resolved: https://github.com/facebook/react-native/pull/28873 Test Plan: N/A Differential Revision: D21519862 Pulled By: shergin fbshipit-source-id: 3dc555bc3aee6bee10127ba5b5862302a63346c4 * Break retain cycle in RCTLegacyViewManagerInteropCoordinator Summary: Breaks retain cycle by having weak reference to bridge. Reviewed By: shergin Differential Revision: D21501419 fbshipit-source-id: 7526ceefceb59e296c6f4944cac5069cb62b33a5 * Migrate Android view managers to type-safe commands generated by JS codegen Summary: ## Changelog: [General] [Changed] - Migrate Android view managers to type-safe commands generated by JS codegen. Reviewed By: JoshuaGross, mdvacca Differential Revision: D21406461 fbshipit-source-id: 93584b240314254675a36a58c4d0c0880d6889fb * Add package name / bundle ID to bundle URL in development Summary: Adds the package name (Android) / bundle ID (iOS) as a new URL parameter named `app` in the bundle URL. This currently has no effect on Metro, which will ignore it for bundling / caching purposes. Changelog: [General] - Add package name / bundle ID to bundle URL in development Reviewed By: cpojer Differential Revision: D21429764 fbshipit-source-id: 394fe50dba72219f7594ebeac9486a8264a836a6 * Forward all bundle URL params through HMR and bundle splitting Summary: Forwards any extra parameters set on the main bundle URL (in development) to the derived bundle URLs used in hot reloading and bundle splitting. Changelog: [General] - Forward URL parameters from main bundle to hot reloaded bundles Reviewed By: cpojer Differential Revision: D21455592 fbshipit-source-id: e1c375e3b6a0f3387372f1d96498dbf7d5237a09 * imp: Add titlePlaceholder in template.config.js (#26218) Summary: In the latest `react-native-cli` we've added more flexibility for setting application title. New config key was introduced - `titlePlaceholder` - which will be changed during init process. PR: https://github.com/react-native-community/cli/pull/650 ## Changelog [General] [Added] - Introduce `titlePlaceholder` for template configuration. Pull Request resolved: https://github.com/facebook/react-native/pull/26218 Test Plan: Initialization works with the default template Differential Revision: D17091492 Pulled By: shergin fbshipit-source-id: 239110f2ad6f817d750575fa366ab49d146b36c1 * Fix opacity not being animated on Text component Reviewed By: shergin Differential Revision: D21523725 fbshipit-source-id: 80be40fd1314b7e1cbaa827ca52f917ba5bc916e * Download image of size based on image view size, not image size Summary: Changelog: [internal] Fabric asks server for images of their real size and scale, not images of size that they would take up on the screen once rendered. Also scale of the screen is incorrect. This causes images to be twice as large as they have to be. Look at the size of the first image size here in Paper, it is `{310.5, 207}`. {F235394066} If you compare it with Fabric, there it is `{800, 381}` {F235394115} It isn't just the size, but scale of request image as well. Fabric always asks for image of scale 1, unlike Paper which takes screen scale into account. Reviewed By: shergin Differential Revision: D21255794 fbshipit-source-id: 9db3ccafec1c09cedc5db5ac0a435a28a4c30c85 * Fabric: Fixed incorrect memory management in Scheduler Summary: The previous implementation was incorrect causing a memory leak. In the new approach, we use a shared pointer to optional to be able to construct that ahead of time and then fill with actual value. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21516794 fbshipit-source-id: ddcbf8a1ad2b1f629ae4ff2842edeb7bb2a275a6 * Fixing typo in comment (#28882) Summary: I was reading the code and noticed the typo. ## Changelog Doesn't affect the code. [General] [Fixed] - Message Pull Request resolved: https://github.com/facebook/react-native/pull/28882 Differential Revision: D21529436 Pulled By: shergin fbshipit-source-id: 82929a6f1fe7b5510ee17fff547aae9d5125891c * iOS: Fix Animated image crash when CADisplayLink target in RCTWeakProxy is nil Summary: ## Problem When self is nil, this may crash in RCTUIImageViewAnimated.m. ``` _displayLink = [CADisplayLink displayLinkWithTarget:[RCTWeakProxy weakProxyWithTarget:self] selector:selector(displayDidRefresh:)]; ``` ## Fix Replace `RCTWeakProxy` with a concrete class `RCTDisplayWeakRefreshable` that has the displayDidRefresh method, that calls the displayDidRefresh method in its weak target. ### Original Github Issue https://github.com/facebook/react-native/pull/28070#issuecomment-619295254 Changelog: [iOS] [Fixed] - Fix Animated image crash when CADisplayLink target in RCTWeakProxy is nil Reviewed By: shergin Differential Revision: D21419385 fbshipit-source-id: da7c3c38f81ea54f633da7f59359e07680ea2faf * Disable animations on Android again Summary: We don't yet have native driver support for animations in bridgeless mode on Android, which leads to some weird bugs (like an overlay that never disappears). Let's just disable animations on Android again. Reviewed By: mdvacca Differential Revision: D21537982 fbshipit-source-id: b4e8882a414fecbd52dd25e02325b5c588ee68c0 * Get redbox working in bridgeless mode, disable logbox Summary: This enables redbox in bridgeless mode, by removing a dep on the bridge. It also disables full screen logbox, since I couldn't figure out how to get it working without the bridge. Reviewed By: rickhanlonii, ejanzer Differential Revision: D21440233 fbshipit-source-id: cb1730fefe1639135fdf06039031975d53f95229 * Add dark mode to loading progress Summary: This diff updates the loading banner to respect the RCTAppearance dev mode setting. Changelog: [General] [iOS] Add dark mode support to loading banner Reviewed By: fkgozali Differential Revision: D21429148 fbshipit-source-id: d7d9e778245112a19accf813dcff693f0d187a38 * Cleanup logbox imports Summary: Was looking at removing logbox from CoreModules, realized it had a bunch of extra deps. Changelog: [Internal] Reviewed By: rickhanlonii Differential Revision: D21535374 fbshipit-source-id: 2427cc66fa34635b49ac4b4fafaf0b6481f5687d * Fix precision of TextInlineViews in Android Summary: TextInlineViews in Android was incorrectly converting values to from float to int, this produced to loose precision and to render incomplete texts in some components. This diff changed the types from int to float, avoiding loose precision. The impact of this bug is not that high because in the conversion to int we were using Math.ceil(), which was already rounding the result to the next pixel. changeLog: [Android][Fixed] Fix precision of TextInlineViews in Fabric Android Reviewed By: JoshuaGross, shergin Differential Revision: D21541159 fbshipit-source-id: 4741ab96964c35af1c1b7d3e821e505ecef2efce * When debugger is attached continue to receive console messages Summary: I noticed an issue when the hermes debugger was attached. Console messages would no longer appear in the terminal. This is because the hermes inspector overrides the console object with its own to intercept messages and send them to the debug client. With this change I made it so that messages are sent to the original console as well instead of completely replacing it. Changelog: [General][Fixed] - When Hermes debugger is enabled continue to send log messages to the console Reviewed By: mhorowitz Differential Revision: D21509895 fbshipit-source-id: 6d91c4b82682e404679533be14b3e5f12e687e79 * Add support for ScrollView.centerContent in Fabric Summary: Changelog: [Internal] ScrollView didn't support centerContent prop. The implementation is copied over from Paper. I added an example of this prop in RNTester. Reviewed By: JoshuaGross Differential Revision: D21548270 fbshipit-source-id: 28f6c8d769c5a6bc1d111b33970a06b95c259132 * Pressable: Rename pressRectOffset to pressRetentionOffset to be consistent with other touchables Summary: Text and the other Touchables have this prop called pressRetentionOffset. Pressable should be consistent with that. Changelog: [Breaking][General]: Pressable: Rename pressRectOffset to pressRetentionOffset to be consistent with other touchables Reviewed By: yungsters Differential Revision: D21552255 fbshipit-source-id: 31e64bad9e48ac98e4934dd2f4c0a7f526de5cb6 * Back out "Fabric: Calling JSVM GC on memory pressure event on iOS" Summary: Reverted for now because it causes a crash. Original commit changeset: 46e32de0f108 (D21484773) Changelog: [Internal] Reviewed By: sammy-SC, kacieb Differential Revision: D21555488 fbshipit-source-id: 555a143fe8cf4c8806d910803f104271454f5797 * LayoutAnimations: have each Props struct do its own interpolation Summary: Each ComponentDescriptor becomes capable of doing its own interpolation over props for animation purposes. This new custom interpolator is called by default by the ConcreteComponentDescriptor, but `ComponentDescriptor::interpolateProps` is a virtual function and each ComponentDescriptor can provide custom interpolation if necessary. For now, only View does any actual interpolation, to support LayoutAnimations. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D20965310 fbshipit-source-id: e1c1588107848e94c155efecb0da1cc1619ae544 * Extend ReactShadowNode API to expose flex props Summary: This diff extends the ReactShadowNode API to expose flex props, this is going to be used by some components that require access to it changeLog: [Android][Added] Exposed getFlex method as part of ReactShadowNode API Reviewed By: JoshuaGross Differential Revision: D21554663 fbshipit-source-id: 26c9a3fe5f72a84120b16b553ab08231817c0efa * Fabric: Unifying interface of stub TextLayoutManager with actual ones Summary: The interface of css/TextLayoutManager now matches acual platform-specific implementations, so now CXX tests compiles and usable. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21518656 fbshipit-source-id: cece3bea14c70410eea22abafb424f7a2cb201c0 * Delete playTouchSound from UIManagerModule Summary: This diff deletes the deprecated PlayTouchSound method from UIManagerModules. I verified there are no callsites of this method in Facebook sourcecode changelog: [BREAKING][Android] Deletes the method PlayTouchSound method from UIManagerModule, this method was moved to the SoundManagerModule class. Motivation: I'm cleaning up my fabric backlog before lockdown Reviewed By: JoshuaGross, TheSavior Differential Revision: D21487612 fbshipit-source-id: f630e2b7f927e0b607a30b9f4904feb63a561ab9 * Revisit deprecated methods in UIManagerModule and update javadoc Summary: Ez diff that revisits deprecated methods in UIManagerModule and update javadoc Motivation: I'm cleaning up my fabric backlog before lockdown changelog: [Android] Update documentation of UIManagerModule methods Reviewed By: JoshuaGross Differential Revision: D21487609 fbshipit-source-id: 896ae21e02d5b1aa57b7158d714986fd1f8c9c5c * Fix dev tool hotkets in RNTester Summary: This diff fixed hotkeys for dev tools like cmd+d, cmd+r, and cmd+i when Turbo Modules are enabled until we have a proper way to eagerly initialize turbo modules. Changelog: [Internal] Reviewed By: RSNara Differential Revision: D21517482 fbshipit-source-id: 8c68bc126361aa961a4f05c93dc19ada32fe44c7 * Introduce NativeModulePerfLogger Summary: ## Description This diff introduces `NativeModulePerfLogger`, its BUCK, Cocoapod, android-ndk targets. This diff also wires up those targets into the React Native bridge and TurboModules targets, so that we get signal on if the code compiles. This diff also introduces `TurboModulePerfLogger`, which is a namespace that holds the `NativeModulePerfLogger` that'll do perf-logging for TurboModules. ## How will perflogging work on iOS? 1. Each application will, during React Native initialization, create a NativeModule perf logger. 2. If TurboModules are enabled, we'll call `TurboModulePerfLogger::setInstance(perfLogger)`. If TurboModules are disabled, we'll call `NativeModulePerfLogger::setInstance(perfLogger)`. 3. TurboModules, when they're created and used, will log events via `TurboModulePerfLogger::getInstance()`. NativeModules (i.e: bridge modules), when they're created and used, will log events via the `NativeModulePerfLogger::getInstance()`. > **Note:** The NativeModule system will log events for non-TurboModules as well. Maybe we should log events for only NativeModules that conform to the `TurboModule` interface, when TurboModules are disabled. This'll ensure a fair comparison between the two systems. ## How will perflogging work on Android? Please see the subsequent diff. allow-large-files Changelog: [Both][Added] - Introduce `NativeModulePerfLogger` Reviewed By: PeteTheHeat Differential Revision: D21318053 fbshipit-source-id: 6ddf5b5a80bdc4076d2dd6588067e2b0ec8c2c6b * Switch TurboModules over to NativeModulePerfLogger Reviewed By: PeteTheHeat Differential Revision: D21363243 fbshipit-source-id: 9836b6651107c924ab9ab8e1ed73b156aed58d9f * Instrument RCTModuleData create Summary: `RCTModuleData` holds our NativeModule classes/objects. This diff instruments `RCTModuleData` create. Changelog: [Internal] Reviewed By: PeteTheHeat Differential Revision: D21415433 fbshipit-source-id: 7738f763c185e20f756d9bb2eff4a9493cde74e8 * Instrument module create Summary: `RCTModuleData instance` is the entry-point for creating and initializing NativeModules on iOS. This diff instruments module-create for the legacy NativeModule system. Changelog: [Internal] Reviewed By: PeteTheHeat Differential Revision: D21415435 fbshipit-source-id: 8554e41cba9105ef528a9a63c49042b99ebf8751 * Instrument JS requires Summary: This diff instruments two markers: - JSRequireBeginning: From the start of the JS require to when we start creating the platform NativeModule - JSRequireEnding: From the end of platform NativeModule create to the end of the JS require In order to accomplish this, I had modify `ModuleRegistry::ModuleRegistry()` to accept a `std::shared_ptr`. I also had to implement the public method `ModuleRegistry::getNativeModulePerfLogger()` so that `JSINativeModules` could start logging the JS require beginning and ending. Changelog: [Internal] Reviewed By: PeteTheHeat Differential Revision: D21418803 fbshipit-source-id: 53828817ae41f23f3f04a95b1d3ac0012735da48 * Instrument async method call batch preprocessing Summary: NativeModule async method calls are queued up on the JS side, and flushed to C++ on every Native -> JS call. Before we execute the batch of async NativeModule method calls, we convert it (a JS object) from a `jsi::Value` to a `folly::dynamic` object in `JSIExecutor::callNativeModules`. Then, in `JsToNativeBridge::callNativeModules`, we convert this `folly::dynamic` object into an `std::vector`, before finally looping over these `MethodCall`s and invoking each NativeModule async method call. The markers I'm adding in this diff measure this `jsi::Value -> folly::dynamic -> std::vector` pre-processing. Changelog: [Internal] Reviewed By: PeteTheHeat Differential Revision: D21435455 fbshipit-source-id: 4c5a9e2b73c1a2a49d7a8f224a0d30afe3a0c79c * Instrument sync and async method calls (#28893) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/28893 `JSIExecutor::callSerializableNativeHook` converts the arguments from `JSI::Value` to `folly::dynamic`. Then, `RCTNativeModule` converts the arguments from `folly::dynamic` to ObjC data structures in its `static invokeInner` function. Therefore, I decided to start the sync markers inside `JSIExecutor::callSerializableNativeHook`, which required me to expose these two methode `ModuleRegistry::getModuleName` and `ModuleRegistry::getModuleSyncMethodName`. This shouldn't modify performance because we eagerly generate a NativeModule's methods when it's first required. So, at worst, this is doing a cache lookup. Changelog: [Internal] Reviewed By: PeteTheHeat Differential Revision: D21443610 fbshipit-source-id: 67cf563b0b06153e56e63ba7e186eea31eafc853 * Remove branching for optimized differ QE Summary: Changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21556312 fbshipit-source-id: 0d6d275de2d691cb42e5e70e5bf19bcc983cae12 * throw std::logic_error instead of aborting the process and convert to java exception Summary: Changelog: [Internal][Yoga] throw std::logic_error instead of aborting the process and convert to java exception for jni layer Reviewed By: pasqualeanatriello Differential Revision: D21301235 fbshipit-source-id: 148b27920e62990a271e1d0df8c85a2cc42f4fd4 * Add support for ScrollView.contentOffset Summary: Changelog: [Internal] Fabric's scrollview didn't have `contentOffset` implemented. Reviewed By: JoshuaGross Differential Revision: D21573179 fbshipit-source-id: 258c1cfa3398336f74d7ab033d90edcec7095292 * Use east const in ScrollViewProps Summary: Changelog: [Internal] For consistency, switching west const to east const. Reviewed By: JoshuaGross, mdvacca Differential Revision: D21574239 fbshipit-source-id: eb3459c63f731f51b24f40f9f80b574661ffd935 * Use vector.empty() to check for emptyness Summary: Changelog: [Internal] Using `empty()` vs `size() == 0` or `size() > 0`. It is a more semantic way to check whether container is empty or not. Reviewed By: JoshuaGross Differential Revision: D21573183 fbshipit-source-id: b83283f687432a037941852114717a0f014e28db * Support interpolating `transform` View property Summary: For Fabric LayoutAnimations, we need to support interpolating the Transform property (which really ends up just being interpolation of ScaleX, ScaleY, or ScaleXY transforms - not arbitrary matrices). To support that, we need to be able to convert Transform back to folly::dynamic, and on the Java side we need to support accepting arbitrary matrices instead of transform maps of properties. Changelog: [Internal] Fabric-only changes Reviewed By: sammy-SC Differential Revision: D21564590 fbshipit-source-id: b137f659b27e4b8fae83921a28ccf46035e18651 * Rename to Summary: ## Motivation This rename will fix the following CircleCI build failures: - [test_ios_unit_frameworks](https://circleci.com/gh/facebook/react-native/150473?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link) - [test_ios_detox_frameworks](https://circleci.com/gh/facebook/react-native/150474?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link) ## Investigation We have 4 podspec targets that map to the same header namespace (i.e: `header_dir`) `ReactCommon`: - **New:** `React-perflogger`: Directory is `ReactCommon/preflogger`, and contains `NativeModulePerfLogger.{h,cpp}`. - `React-runtimeexecutor`: Directory is `ReactCommon/runtimeexecutor`, and contains only `RuntimeExecutor.h` - `React-callinvoker`: Directory is `ReactCommon/callinvoker`, and contains only `CallInvoker.h` - `ReactCommon/turbomodule/core`: Directory is `ReactCommon/turbomodule`, and contains C++ files, as well has header files. **The problem:** We couldn't import headers from `React-perflogger` in `ReactCommon/turbomodule/core` files. **The cause:** I'm not entirely sure why, but I was able to discern the following two rules by playing around with the podspecs: 1. If your podspec target has a cpp file, it'll generate a framework when `USE_FRAMEWORKS=1`. 2. Two different frameworks cannot map to the same `module_name` or `header_dir`. (Why? No clue. But something breaks silently when this is the case). So, this is what happened when I landed `React-perflogger` (D21443610): 1. The TurboModules code generates the `ReactCommon` framework that uses the `ReactCommon` header namespace. 2. `React-runtimeexecutor` and `React-callinvoker` also used the `ReactCommon` header namespace. However, neither generate a framework because of Rule 1. 3. When I comitted `React-perflogger`, I introduced a second framework that competed with the `ReactCommon` framework (i.e: TurboModules code) for the `ReactCommon` header namespace. Rule 2 violation. ## Thoughts on renaming - `` is too generic, and the `perflogger` namepsace is used internally within FB. - `` matches our fabric header format, but I'm pretty sure that slashes aren't allowed in `header_dir`: I tested this and it didn't work. IIRC, only alphanumeric and underscore are valid characters for `header_dir` or `module_name`. So, I opted to just use `reactperflogger`. Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D21585006 fbshipit-source-id: e3339273af5dfd65a1454d87213d1221de6a4651 * Create new Feature flag to eager initialize Fabric Summary: This diff exposes a new react feature flag to eager initialize fabric changelog: [Internal] Internal change in Fabric Reviewed By: JoshuaGross Differential Revision: D21574814 fbshipit-source-id: c74fb316963fe92e43ce0ca6262cb73a6a4acb7f * Implement eager initialization of Fabric Summary: This diff implements the eager initialization of fabric based on the param created in previous diffs changelog: [Internal] Internal change in Fabric Reviewed By: JoshuaGross Differential Revision: D21574815 fbshipit-source-id: 1dfd2611ce8c8529ce5f6a7a8c48f8bee19be256 * Extend Binding to log only when a MC is enabled Summary: This diff integrates the logging of Binding class using a MC changelog: [Internal] Internal change to control logging of Fabric Reviewed By: JoshuaGross Differential Revision: D21574813 fbshipit-source-id: e7b2acbaa4cb8a8e748db91af5c6960cd47b520e * Fix position of TextInlineViews when nesting multiple Text components Summary: This diff fixes the position of TextInlineViews when nesting multiple Text. The root is that we were not taking into consideration LayoutOffset of nested TextViews during the calculation of the nested views. changelog: [Internal] Internal fix in Fabric Reviewed By: JoshuaGross Differential Revision: D21586893 fbshipit-source-id: 55e6ad0cf95222588ffe9185f5e22baea1059448 * Revert D21585006: Rename to Differential Revision: D21585006 Original commit changeset: e3339273af5d fbshipit-source-id: cb4ff227edcc16842c7539bf71c912cd4ec478e0 * JS: Fix Spelling of JavaScript Summary: Fixes some misspellings of JavaScript. Changelog: [Internal] Reviewed By: cpojer Differential Revision: D21536786 fbshipit-source-id: d5551dfbb3895d0806d31ba38ecaeeeb7843bf20 * Enabling [-Werror,-Wunused-property-ivar] (#28895) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/28895 ## Summary Enabling [-Werror,-Wunused-property-ivar] ## Changelog [Warning] [-Werror,-Wunused-property-ivar] Enable the warning ## Test Plan Build Reviewed By: jdthomas Differential Revision: D20961613 fbshipit-source-id: 8ec73935384800581a71ad96957b716a0d894152 * Add support for Debugger.runIfWaitingForDebugger Summary: This call is used to continue execution when the app has just been started in a "wait for debugger" mode. This is the only case in which it has an effect. Notably, it should do nothing in the following cases, which a layperson may be tempted to classify as "WaitingForDebugger": * The app was running detached and hit a 'debugger;' statement * The app is paused because of a breakpoint or hitting the Pause button * The app stopped on an instrumentation breakpoint, and expects the debugger to collect data and potentially auto-resume. Changelog: [Internal] Add Hermes support for Debugger.runIfWaitingForDebugger Reviewed By: mhorowitz Differential Revision: D21557446 fbshipit-source-id: 790cec7444ddc61908d2ef9d92e4649b535d678f * iOS: Fix image instrumentation lifecycle on image cancel Summary: Internal loggers were not deallocated when images were canceled on RCTImageView Reviewed By: fkgozali Differential Revision: D21380284 fbshipit-source-id: 00440cf49708ec03ecd7d9268001aa458ccbf923 * Rename to Summary: ## Motivation This rename will fix the following CircleCI build failures: - [test_ios_unit_frameworks](https://circleci.com/gh/facebook/react-native/150473?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link) - [test_ios_detox_frameworks](https://circleci.com/gh/facebook/react-native/150474?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link) ## Investigation We have 4 podspec targets that map to the same header namespace (i.e: `header_dir`) `ReactCommon`: - **New:** `React-perflogger`: Directory is `ReactCommon/preflogger`, and contains `NativeModulePerfLogger.{h,cpp}`. - `React-runtimeexecutor`: Directory is `ReactCommon/runtimeexecutor`, and contains only `RuntimeExecutor.h` - `React-callinvoker`: Directory is `ReactCommon/callinvoker`, and contains only `CallInvoker.h` - `ReactCommon/turbomodule/core`: Directory is `ReactCommon/turbomodule`, and contains C++ files, as well has header files. **The problem:** We couldn't import headers from `React-perflogger` in `ReactCommon/turbomodule/core` files. **The cause:** I'm not entirely sure why, but I was able to discern the following two rules by playing around with the podspecs: 1. If your podspec target has a cpp file, it'll generate a framework when `USE_FRAMEWORKS=1`. 2. Two different frameworks cannot map to the same `module_name` or `header_dir`. (Why? No clue. But something breaks silently when this is the case). So, this is what happened when I landed `React-perflogger` (D21443610): 1. The TurboModules code generates the `ReactCommon` framework that uses the `ReactCommon` header namespace. 2. `React-runtimeexecutor` and `React-callinvoker` also used the `ReactCommon` header namespace. However, neither generate a framework because of Rule 1. 3. When I comitted `React-perflogger`, I introduced a second framework that competed with the `ReactCommon` framework (i.e: TurboModules code) for the `ReactCommon` header namespace. Rule 2 violation. ## Thoughts on renaming - `` is too generic, and the `perflogger` namepsace is used internally within FB. - `` matches our fabric header format, but I'm pretty sure that slashes aren't allowed in `header_dir`: I tested this and it didn't work. IIRC, only alphanumeric and underscore are valid characters for `header_dir` or `module_name`. So, I opted to just use `reactperflogger`. Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D21598852 fbshipit-source-id: 60da5d0f7758eaf13907a080b7d8756688f40723 * Detect recoverable cases where SendAccessibilityEvent exceptions don't need to hard-crash the app Summary: See T53113342, view command retry logic, and comment explanation in code. Changelog: [Internal] fabric Reviewed By: mdvacca Differential Revision: D21606911 fbshipit-source-id: 503f52400beb98a19840c67896e0a7a519f30573 * FabricUIManager should only swallow exceptions related to ViewCommands Summary: In the previous diff I made a few more things "Retryable" exceptions, where previously only strictly ViewCommand-related code would throw Retryable exceptions. This change is to prevent FabricUIManager from swallowing these exceptions if they happen outside of the context of ViewCommands. Changelog: [Internal] Fabric Reviewed By: mdvacca Differential Revision: D21607324 fbshipit-source-id: b3bad4694d2399db447a9117cc31169104b36de5 * Move Size check before accessing type of transform Summary: Changelog: [Android][Fixed] - Move Size check before accessing type of transform Reviewed By: JoshuaGross, kacieb Differential Revision: D21605800 fbshipit-source-id: eb3eb3c3e2992e2e117e6e0af53fbe939e09d971 * use xplat BUCK attribution Summary: Internal code attribution update. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21603406 fbshipit-source-id: c3da1823e26beb0092d97e66d731618c0433a2f7 * Create new ReactFlag to configure initialization of Fabric Summary: EZ diff to expose a new ReactFlag that will be used to configure the initialization of Fabric changelog: [Internal] Internal change in fabric Reviewed By: JoshuaGross Differential Revision: D21606972 fbshipit-source-id: 53d6bac673b95f0fae93262ff52b815d76bb59ab * Use MC to Start Fabric surfaces using layoutMetrics or not Summary: This diff uses a new MC to Start Fabric surfaces using layoutMetrics or not. The motivation is to verify if the new initialization of fabric surfaces is regressing in production changelog: [Internal] Internal change in fabric Reviewed By: JoshuaGross Differential Revision: D21606971 fbshipit-source-id: ed1f6937ffd0f1e6c54e3ebc34595d75b6c5f6e1 * Fabric: Automatic removing outstanding Surface on Scheduler destruction; gated. Summary: This is an addition to an automatic emergency clean-up algorithm that we have in Scheduler. In addition to committing empty surfaces, we also remove those surfaces from the registry making calling stuff on them impossible. Removing surfaces waits for all commits in flight to be finished, so it theoretically can deadlock (so we gated that). If we won't face deadlocks in a coming couple of weeks, I would remove gating. Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D21610683 fbshipit-source-id: 71feeaa0ee4521a0180cdfba6e3a271e7f7d9401 * Pressable: Add Support for Inspector Overlay Summary: Adds support for the debug overlay (enabled via the Inspector) that the legacy touchable components supported. Changelog: [General][Added] - Added Inspector overlay support for Pressable Reviewed By: TheSavior Differential Revision: D21614412 fbshipit-source-id: b884e04f8dba1bfd35e61de25d33d6d47bc34b03 * set`NSAllowsArbitraryLoads` to false by default in template (#28885) Summary: Since we already have `localhost` as`Exception Domains` in NSAppTransportSecurity to allow connect to dev server, the template should set `NSAllowsArbitraryLoads` to false by default, as exaplained in [Apple's document](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloads) as a good practice. ## Changelog [iOS] [enhancement] - set `NSAllowsArbitraryLoads` to `false` by default in template Pull Request resolved: https://github.com/facebook/react-native/pull/28885 Differential Revision: D21624042 Pulled By: shergin fbshipit-source-id: 690afcd037c3f328d293ef4475380a28efd9fee6 * Extends view flattening algorithm to support flattening of absolute positioned views Summary: This diff extends view flattening algorithm to support flattening of absolute positioned views. changeLog:[Internal] internal change in fabric Reviewed By: shergin Differential Revision: D21614519 fbshipit-source-id: e33a41677a332bb58d5e5dcedbffd614a8416a45 * Update Hermes attribution labels Summary: Internal target attribution update. Changelog: [Internal] Reviewed By: janettec Differential Revision: D21519089 fbshipit-source-id: 1aa1b57f5e1a1405db32f5cfb9973f27a13bcfdf * Fabric: Changes in LayoutableShadowNodeTest Summary: One small test was added. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21480201 fbshipit-source-id: fd6c050143fcdf27d345ee62e74c4368266e6ce0 * Fabric: Refinement of `LayoutableShadowNode::getRelativeLayoutMetrics` Summary: This diff simplifies the implementation of `LayoutableShadowNode::getRelativeLayoutMetrics`. It fixes a small bug but the most important change is the new interface. Now the function that does measurements accepts a node and a family instead of two nodes. It prevents misuse and misinterpretation of what the function does. The function needs two things to perform measurement: * an ancestor node that defines the tree is being measured and the base node of measurement; * a family of some descendant node being measured relative to the ancestor node. An API that accepts two nodes is misleading because it implies that the given descendant node will be measured (which is not true). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21480200 fbshipit-source-id: 9fddc361417fee47bbf66cc7ac2954eb088a3179 * Fabric: Simplification of `UIManager::getRelativeLayoutMetrics` Summary: Using of the new API makes clear that we don't need to calculate the newest descendant node. Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D21480202 fbshipit-source-id: c54998573baffe4a05726e3402da027f052b049a * Basic implementation of ARTSurfaceView Summary: Basic implementation of ARTSurfaceView (shadow nodes, props and component descriptor) changelog: [Internal] Reviewed By: shergin Differential Revision: D21621484 fbshipit-source-id: 5577989e966a8a97a043454bf9ae1e5602abc3b1 * Create basic implementation of Shape Summary: Create basic implementation of Shape (shadow node, props and component descriptor) changelog: [Internal] Internal changes to support art in Fabric Reviewed By: shergin Differential Revision: D21621482 fbshipit-source-id: e5b9bb2812ee92bce625301b7521f0578eaca0ff * Basic implementation of ARTGroupProps Summary: Basic implementation of ARTGroupProps (shadow node, props and component descriptor) changelog: [Internal] Internal changes to support art in Fabric Reviewed By: shergin Differential Revision: D21621480 fbshipit-source-id: 367a479568b8c1a290f3e0f633cc4052a9c95b87 * Basic implementation of ARTText Summary: Basic implementation of ARTText (shadow node, props and component descriptor) changelog: [Internal] Internal changes to support art in Fabric Reviewed By: shergin Differential Revision: D21621483 fbshipit-source-id: d0886dc149520af13faa1bb936dfcccab1798c37 * iOS: Fix logging lifecycle when image is scrolled out and immediately back in Reviewed By: fkgozali Differential Revision: D21619910 fbshipit-source-id: b94073afaacad45e12d22d593184cea97612fa26 * Delete local data from Fabric android Summary: LocalData was fully replaced by State, this diff removes dead code thas was previously used to update local Data changelog: [Internal] Internal cleanup on Fabric Android code Reviewed By: shergin Differential Revision: D21621481 fbshipit-source-id: a3e38300a54a85adff9145cdeea1e89dad09103f * get ripple drawables by id (#28600) Summary: While working on recent PRs regarding ripple radius in TouchableNativeFeedbaack and ripple support in Pressable I noticed `ReactDrawableHelper` uses a [discouraged](https://developer.android.com/reference/android/content/res/Resources#getIdentifier(java.lang.String,%20java.lang.String,%20java.lang.String)) way to obtain resources. The attribute names (strings) `'selectableItemBackground'` and `'selectableItemBackgroundBorderless'` are used here https://github.com/facebook/react-native/blob/4a48b021d63a474f1570e92616988384957d4273/Libraries/Components/Touchable/TouchableNativeFeedback.js#L105 And passed to `context.getResources().getIdentifier()` in `ReactDrawableHelper`. Since we know the attribute names beforehand I figured we can obtain the resources by id (fast) instead of by name (slow). I made it so that the slow code path is taken in case the attribute name does not match what is expected, as a fallback. Note that I did not do any measurement of the effect of this, I'm just offering this as a PR. You'll notice that this PR relies on the fact that the string in JS is the same as the string in Java (it is duplicated). While I could export the strings from Java and use them in JS, I wasn't sure where to export them. But note that even before, the JS code depended on the `'selectableItemBackground'` and `'selectableItemBackgroundBorderless'` strings to exist on the native side, in the android SDK, I just made the dependency explicit. ## Changelog [Android] [Changed] - get ripple drawables by id Pull Request resolved: https://github.com/facebook/react-native/pull/28600 Test Plan: tested manually in RNTester Differential Revision: D21241773 Pulled By: shergin fbshipit-source-id: 1b8314f99616095cb6ed557c62095cf3200f53b6 * Pressable: Minimum Press Duration Summary: When a `Pressable` has a configured (or the default) `delayPressIn` and no (or the default) `delayPressOut`, tapping very quickly can lead to intantaneous invocation of `onPressIn` and `onPressOut`. The end result is that users may never experience any intended visual press feedback. This changes `Pressable` to accept (and be preconfigured with a default) **minimum press duration**. The minimum press duration ensures that even if the press is released before `delayPressIn` has elapsed, `onPressOut` will still wait the remaining time up to `minPressDuration` before firing. Note that setting a non-zero `delayPressOut` is insufficient because if a user holds down on a `Pressable` for longer than `delayPressIn`, we still want `onPressOut` to fire immediately when the press is released. Changelog: [General][Changed] - Added `minPressDuration` to `Pressable`. Reviewed By: TheSavior Differential Revision: D21614708 fbshipit-source-id: 502f3d8ad6a40e7762435b6df16809c8798dd92c * Add possibility to disable buttons in action sheet ios (#28792) Summary: I've noticed that currently there is no option to disable button within the `ActionSheetIOS`. It can be really useful and decided to extend the API to support that functionality. I added a new option called `disabledButtonsIndices` to `ActionSheetIOS` which is an array of button indices which should be disabled. `ActionSheetIOS` documentation - PR https://github.com/facebook/react-native-website/pull/1898 ## Changelog [iOS] [Added] - Add disableButtonsIndices option to ActionSheetIOS component Pull Request resolved: https://github.com/facebook/react-native/pull/28792 Test Plan: 1. Run the `RNTester` 2. Choose `ActionSheetIOS` 3. Check the fourth example `Show Action Sheet with disabled buttons` 4. `Option 1` and `Option 2` should be disabled screenshot | gif --- | --- Screenshot 2020-04-30 at 15 16 22 | ![action_sheet_disabled](https://user-images.githubusercontent.com/22746080/80739043-24227200-8b16-11ea-8bcb-af25eb57baac.gif) Differential Revision: D21396409 Pulled By: shergin fbshipit-source-id: b3c3e442965160e0c5e52854352f0540575c4d4c * deploy Flow 0.125.1 to xplat Summary: Changelog: [Internal] allow-large-files Reviewed By: gkz Differential Revision: D21597387 fbshipit-source-id: dddec43885daa5a9c5c4dfe8e338ecedc9abcd1e * Prevent SafeAreaView from reporting same size twice Summary: Changelog: [Internal] # Problem We call `_state->updateState` anytime safe area insets changes. Once that is called, `_state` still holds old value, so when we check whether there is big enough of a difference, we are actually checking it against old value of state until `updateState` is called from MountingManager. This causes `_state->updateState` to be called dozens of times (I measured 47, with this diff it fell to 22) when displaying a safe area view inside Modal. # Solution Create new ivar `_lastPaddingStateWasUpdatedWith` where we store last padding that was sent through `_state->updateState` and compare new inset with this value instead of last value stored in state. Reviewed By: shergin Differential Revision: D21596367 fbshipit-source-id: b9249b8ef444ea532ec8b86a15a32c733eb6f987 * Copy alreadyAppliedPadding when cloning SafeAreaViewShadowNode Summary: Changelog: [Internal] `SafeAreaViewShadowNode.alreadyAppliedPadding` was always {0, 0, 0, 0} because value of previous shadow node was never copied over to new shadow node during clone. Reviewed By: shergin Differential Revision: D21617361 fbshipit-source-id: 6d6c91b19ff60271bf7c48145d85faaee0321680 * remove suppress_comments from xplat flowconfigs Summary: Flow is deprecating this config option, so we are removing it in preparation. Changelog: [Internal] Reviewed By: gkz Differential Revision: D21642915 fbshipit-source-id: cb2abff067b8702b37d5fdbdd63556f464e2a4a5 * Fix thrown GradleScriptExceptions Summary: Changelog: [Android] [Fixed] Use actual constructor when throwing GradleScriptException Reviewed By: PeteTheHeat Differential Revision: D21644385 fbshipit-source-id: 09802682cb9eb788e508cff3fbebdbdacd5b1d69 * Revert D21396409: Add possibility to disable buttons in action sheet ios Differential Revision: D21396409 Original commit changeset: b3c3e4429651 fbshipit-source-id: 073bea94d96f0ebbb474c474c73e4e3f01f27b2e * RN: Persist Asynchronously Consumed Events in Pressability Summary: There are a few places in `Pressability` where we asynchronously consume the `event` object. Most places do not encounter any problems because the actual properties on `event` are seldom used. Nonetheless, we should fix these gaps. Changelog: [General][Fixed] - Fix invalid `event` objects from `onPressOut` in certain cases Reviewed By: rickhanlonii Differential Revision: D21657126 fbshipit-source-id: e28d825b85d25602427beaf0bd603d22eaa5960a * docs: mention prettier in eslint-config README (#28930) Summary: The combination of `eslint` (v7.0.0), `react-native-community/eslint-config` (v1.1.0), and `flow-typed` (v3.1.0) causes the following error (`ESLint couldn't find the plugin "eslint-plugin-prettier"`) because `flow-typed` (v3.1.0) depends on `prettier: ^1.19.1` (c.f. https://github.com/flow-typed/flow-typed/blob/master/cli/package.json#L38). To deal with the error, developers should install `prettier` (v2.x) directly in `devDependencies`. ``` Oops! Something went wrong! :( ESLint: 7.0.0 ESLint couldn't find the plugin "eslint-plugin-prettier". (The package "eslint-plugin-prettier" was not found when loaded as a Node module from the directory "/Users/exkazuu/Projects/test".) It's likely that the plugin isn't installed correctly. Try reinstalling by running the following: npm install eslint-plugin-prettier@latest --save-dev The plugin "eslint-plugin-prettier" was referenced from the config file in ".eslintrc.js » react-native-community/eslint-config". If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team. ``` ## Changelog [Internal] [Changed] - `react-native-community/eslint-config` README recommends developers to install prettier directly Pull Request resolved: https://github.com/facebook/react-native/pull/28930 Test Plan: This PR changes only README, so tests are not required. Differential Revision: D21659672 Pulled By: cpojer fbshipit-source-id: 67c775e664d539815fa78e12574d73eaa1942de1 * Daily `arc lint --take GOOGLEJAVAFORMAT` Reviewed By: zertosh, colriot Differential Revision: D21642196 fbshipit-source-id: 5ca93be472da2630374850e0937bbdcb92f86b31 * Add FBRotatablePhotoPlayerView to LegacyInterop whitelist Summary: Changelog: [internal] Reviewed By: sammy-SC Differential Revision: D21661427 fbshipit-source-id: de692015f3c6f029d2ea3f927e30d6758c0b1102 * Fix assignment of hitTestEdgeInsets in RCTViewComponentView Summary: Changelog: [Internal] If you look at implementation of hit testing in `RCTViewComponentView` ``` - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) { return [super pointInside:point withEvent:event]; } CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets); return CGRectContainsPoint(hitFrame, point); } ``` you will notice that we use `UIEdgeInsetsInsetRect` to calculate hitFrame. The input for this function is bounds and `hitTestEdgeInsets`. `hitTestEdgeInsets` is our hitSlop. Look at documentation of `UIEdgeInsetsInsetRect`, it says "Adjusts a rectangle by the given edge insets.". So if you give it a positive edge insets, it will make the rect smaller. That's why we need to reverse values of hitSlop to negative before assigning it to `hitTestEdgeInsets`. Paper does the same thing here https://github.com/facebook/react-native/blob/d0871d0a9a373e1d3ac35da46c85c0d0e793116d/React/Views/RCTViewManager.m#L304-L305 Reviewed By: mdvacca Differential Revision: D21661894 fbshipit-source-id: c3dd6c55b68e4fdef8589ca8f0484e2837b4136c * C++ Fabric Core LayoutAnimations Summary: This is the V1 implementation of Fabric Core LayoutAnimations. The intention is to structure this in such a way that it's easy for each platform to customize the "AnimationDriver" class (to do platform-specific optimizations) without changing the KeyFrameManager at all. In the future, this structure and architecture should allow us to iterate faster on new animation APIs. TODOs: - Use std::chrono for timekeeping Changelog: [Internal] Support for LayoutAnimations in Fabric Reviewed By: shergin Differential Revision: D17486030 fbshipit-source-id: 95c72cf9fc2b4bf3fe652fbd249cf2ad113033c7 * LayoutImplementations: implement all existing animation curves besides Keyboard Summary: Implement EaseIn, EaseOut, EaseInOut, and Spring with SpringDamping. Note this does not yet implement Keyboard-type animation for iOS (coming soon), and the spring interpolator is VERY naive. We likely want to replace it with a "real" spring animation ASAP. The spring animation is identical to what Android does today, but would likely be a downgrade for iOS. I will do both in a followup. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21587648 fbshipit-source-id: 246ab7fd40397a4231bb6b18d2f29602788a1bd2 * LayoutAnimations: implement LayoutAnimationStatusDelegate for platform-specific integrations Summary: The LayoutAnimationStatusDelegate exists so that platforms can get a signal when animations are starting or have all completed. This signal is meant to be used ONLY for driving animations at 60fps, or stopping that process, on the platform side. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21583109 fbshipit-source-id: 234496841bde226fcd6623c74c1a500e5cd00d99 * Android-specific LayoutAnimation integration Summary: Turn on Fabric LayoutAnimations on Android. I will gate this change behind a QE before landing. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21583122 fbshipit-source-id: 82eacb7192f7c59d2809a67a619cb950293aaace * iOS-specific LayoutAnimation integration Summary: Turn on Fabric LayoutAnimations on iOS. I will gate this change behind a QE before landing. Changelog: [Internal] Reviewed By: shergin Differential Revision: D21583932 fbshipit-source-id: 0e0f988b44af37eb6fb22cccb48b0c7aa5020ca7 * LayoutAnimations: Use Quaternions to interpolate rotation transforms Summary: Implement a real Transform interpolation. It uses quaternions/Slerp to interpolate rotations. This allows us to interpolate scale, rotation, and translation simultaneously. See caveats in code. Because of the way transform matrices work, there isn't much (anything?) we can do about skew, and certain values will look nonsensical. This seems to be true for any variant of this algorithm. This is a big step up from Classic RN which didn't support this in LayoutAnimations at all. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21615829 fbshipit-source-id: da6cb931ce857107d4b3d20108fb9bbecbf4f898 * LayoutAnimations: allow Paragraph props to be interpolated Summary: 1. Split out the prop interpolation function out of the View ComponentDescriptor, into an inline'd function that can be used elsewhere. 2. Call it from View and from Paragraph component descriptors. This causes animations including Text to look normal on iOS. Changelog: [Internal] Reviewed By: shergin Differential Revision: D21635473 fbshipit-source-id: 470f43fd24a6e80d8696ee2f2a09d9e693b7f280 * Revert D21635473: LayoutAnimations: allow Paragraph props to be interpolated Differential Revision: D21635473 Original commit changeset: 470f43fd24a6 fbshipit-source-id: 8507b0aaead2c0ebbdd09077db99e5e44da175ab * Revert D21615829: LayoutAnimations: Use Quaternions to interpolate rotation transforms Differential Revision: D21615829 Original commit changeset: da6cb931ce85 fbshipit-source-id: 4b5bc392a35aac627b89ccccb73e10a2b1c4aaa6 * Revert D21583932: iOS-specific LayoutAnimation integration Differential Revision: D21583932 Original commit changeset: 0e0f988b44af fbshipit-source-id: 2e3986e9310a56880a36cfd72fdee53fd9177dca * Revert D21583122: Android-specific LayoutAnimation integration Differential Revision: D21583122 Original commit changeset: 82eacb7192f7 fbshipit-source-id: 5bcc392cdb3b11c755395beba4032a21c1bf2668 * Revert D21583109: LayoutAnimations: implement LayoutAnimationStatusDelegate for platform-specific integrations Differential Revision: D21583109 Original commit changeset: 234496841bde fbshipit-source-id: 2f74dcce23f4eebf987e2114ad1f23cf01e11a9d * Revert D21587648: LayoutImplementations: implement all existing animation curves besides Keyboard Differential Revision: D21587648 Original commit changeset: 246ab7fd4039 fbshipit-source-id: 427e13ff821776feb4952c3438c15ea815fce5f0 * Revert D17486030: C++ Fabric Core LayoutAnimations Differential Revision: D17486030 Original commit changeset: 95c72cf9fc2b fbshipit-source-id: fa7ef058f5d0dea0154c62718a8a11d9330698d9 * Remove flow-node requirement from native modules codegen script Summary: Use transpiled `react-native-codegen` scripts. By avoiding use of flow-node, we no longer need to upgrade flow-remove-types in order to run the native modules codegen script. The interface for the script remains the same: ``` ./scripts/generate-native-modules-specs.sh [optionalOutputDir] ``` Changelog: [Internal] Reviewed By: TheSavior Differential Revision: D21629705 fbshipit-source-id: 7714a67e36c8151a3b0b49285b6a6c93a525d7bc * C++ Fabric Core LayoutAnimations Summary: This is the V1 implementation of Fabric Core LayoutAnimations. The intention is to structure this in such a way that it's easy for each platform to customize the "AnimationDriver" class (to do platform-specific optimizations) without changing the KeyFrameManager at all. In the future, this structure and architecture should allow us to iterate faster on new animation APIs. Changelog: [Internal] Support for LayoutAnimations in Fabric Reviewed By: mdvacca Differential Revision: D21675808 fbshipit-source-id: b3ef44729bb8b6217f90760aec9737276c9601d1 * LayoutImplementations: implement all existing animation curves besides Keyboard Summary: Implement EaseIn, EaseOut, EaseInOut, and Spring with SpringDamping. Note this does not yet implement Keyboard-type animation for iOS (coming soon), and the spring interpolator is VERY naive. We likely want to replace it with a "real" spring animation ASAP. The spring animation is identical to what Android does today, but would likely be a downgrade for iOS. I will do both in a followup. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675810 fbshipit-source-id: ac98554ebc81f0b81fdacefd6d848e3566e424c0 * LayoutAnimations: implement LayoutAnimationStatusDelegate for platform-specific integrations Summary: The LayoutAnimationStatusDelegate exists so that platforms can get a signal when animations are starting or have all completed. This signal is meant to be used ONLY for driving animations at 60fps, or stopping that process, on the platform side. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675807 fbshipit-source-id: e89ba524ea43c266556199c8854e8228869755e3 * Android-specific LayoutAnimation integration Summary: Turn on Fabric LayoutAnimations on Android. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675809 fbshipit-source-id: 49fbd3094532c5b486ea12a58898b986964ddd6e * iOS-specific LayoutAnimation integration Summary: Turn on Fabric LayoutAnimations on iOS. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675803 fbshipit-source-id: 1be5f516f8af9439134c1688deb63b35b964ae99 * LayoutAnimations: Use Quaternions to interpolate rotation transforms Summary: Implement a real Transform interpolation. It uses quaternions/Slerp to interpolate rotations. This allows us to interpolate scale, rotation, and translation simultaneously. See caveats in code. Because of the way transform matrices work, there isn't much (anything?) we can do about skew, and certain values will look nonsensical. This seems to be true for any variant of this algorithm. This is a big step up from Classic RN which didn't support this in LayoutAnimations at all. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675805 fbshipit-source-id: a33494cc02c73102ca67c1d562efc4b2a7308a4a * LayoutAnimations: allow Paragraph props to be interpolated Summary: 1. Split out the prop interpolation function out of the View ComponentDescriptor, into an inline'd function that can be used elsewhere. 2. Call it from View and from Paragraph component descriptors. This causes animations including Text to look normal on iOS. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21675804 fbshipit-source-id: c34a317749fd6c108aef072d47f3dcb14ce8aa5c * Guard all NativeModulePerfLogger calls with a null check Summary: ## Motivation We got this crash T67304907, which shows a `EXC_BAD_ACCESS / KERN_INVALID_ADDRESS` when calling this line: ``` NativeModulePerfLogger::getInstance().asyncMethodCallBatchPreprocessStart(); ``` There are no arguments in that call, so I figured the only error could be when we try to invoke `getInstance()` or `asyncMethodCallBatchPreprocessStart()`. This diff: 1. Removes the `NativeModulePerfLogger::getInstance()` bit. Now NativeModulePerfLogger is used via regular static C functions. So, there's no way that simply invoking one of the logging functions crashes the application: there's no vtable lookup. 2. Inside each logging function, when perf-logging is disabled, the global perflogger should be `nullptr`. This diff makes it so that in that case, we won't execute any code in the control group of the perf-logging experiment. ## Changes **How do we enable NativeModule perf-logging?** - Previously: - `NativeModulePerfLogger::setInstance(std::make_shared(...))` - `TurboModulePerfLogger::setInstance(std::make_shared(...))`. - Now: - `BridgeNativeModulePerfLogger::enableLogging(std::make_unique(...))` - `TurboModulePerfLogger::enableLogging(std::make_unique(...))` **How do we do NativeModule perf-logging now?** - Previously: - `NativeModulePerfLogger::getInstance().command(...args)` - `TurboModulePerfLogger::getInstance().command(...args)`. - Now: - `BridgeNativeModulePerfLogger::command(...args)` - `TurboModulePerfLogger::command(...args)`. The benefit of this approach is that each method in `BridgeNativeModulePerfLogger` is guarded with an if check. Example: ``` void moduleCreateConstructStart(const char *moduleName, int32_t id) { NativeModulePerfLogger *logger = g_perfLogger.get(); if (logger != nullptr) { logger->moduleCreateConstructStart(moduleName, id); } } ``` Therefore, we don't actually execute any code when perf-logging is disabled. Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D21669888 fbshipit-source-id: 80c73754c430ce787404b563878bad146295e01f * Create classes to represent C++ state of ART Summary: This diff introduces a set of classes that are going to be used to represent the internal State of ART nodes changeLog: [Internal][Android] Internal change to support ART in Fabric Reviewed By: JoshuaGross, shergin Differential Revision: D21657612 fbshipit-source-id: ea6d94b06807ff02d222dfa129a1cae384dceeaa * Create internal State of ART based on Shadow Nodes Summary: This diff creates the internal state of ART based on its shadow nodes changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21657607 fbshipit-source-id: 0a15e90ee7465bf3a2b1001ff9d3198eb22fd708 * Integrate State into ARTSurfaceViewShadowNode Summary: This diff integrates ART state into ARTSurfaceViewShadowNode changelog: [Internal] Reviewed By: shergin Differential Revision: D21657611 fbshipit-source-id: 06bd4d610e2c52e0ef3bca423b93c9ad2318e8df * Integrate ElementType Enum into state Summary: This diff replaces the usage of int to represent the type of elements for an Enum changelog: [Internal] Internal change to support ART in fabric Reviewed By: shergin Differential Revision: D21657706 fbshipit-source-id: 7bda0210d50136477f0524695d5406e35074f09c * Serialize ART state into folly::dynamic Summary: This diff serializes ART state into folly::dynamic. this is necessary to send this data to Android changelog: [Internal] Reviewed By: shergin Differential Revision: D21657608 fbshipit-source-id: 6c1b69af7d1dbe7de15e509f83c508a38294d89e * Prevent ABA problem during YogaLayoutableShadowNode cloning Summary: Changelog: [Internal] # Problem Yoga internally uses address of owner to determine whether a node should be cloned or it shouldn't. During layout, it traverses the tree and looks whether current parent is equal to child's owner. If it isn't it means the yoga node is shared between 2+ trees and need to be cloned before mutated. Parent in yoga is stored as reference to the parent. We can run into an issue where yoga node is shared between 2+ trees, but its current parent is equal to child's owner. This is because we reallocate new parent at address where its previous parent lived. This happens over few iterations, the old parent is first deallocated. This is known as ABA problem. # Solution When we are cloning node, we loop over all children to see whether any of the is not using address of new `yogaNode_` as its owner. At this point we know this is accidental and set its owner to `0xBADC0FFEE0DDF00D`. We chose `0xBADC0FFEE0DDF00D` because when someone is debugging this in LLDB and prints this address, it will hint them that it was artificially set and can string search for it in the codebase. Reviewed By: shergin Differential Revision: D21641096 fbshipit-source-id: c8b1b4487ea02b367f5831c1cdac055bce79c856 * Daily `arc lint --take CLANGFORMAT` Reviewed By: zertosh Differential Revision: D21683227 fbshipit-source-id: a29bc66af62fe99d803ac386261269b5cd16c18f * Fix order in which views are mounted in RCTLegacyViewManagerInteropComponentView Summary: Changelog: [Internal] # Problem We were recording mount child component calls with its arguments and the replaying them inside `finalizeUpdates`. However we store the events in NSDictionary where `key` was index at which the child should be added. Then in `finalizeUpdates` we iterated over this NSDictionary and added those views into our paper view. `NSDictionary` is unordered, it isn't guaranteed what was first inserted into it, will be first iterated over. # Solution Use `NSMutableArray` instead which guarantees order. Reviewed By: shergin Differential Revision: D21685993 fbshipit-source-id: 3b933f05125130eef175d7a8a56f29012ee76bb3 * Separate event dispatchers to ivars instead of array Summary: Changelog: [Internal] Separates EventQueues from an array into 4 ivars. EventQueues are of different type, in the future we will want to call different methods on different kind of EventQueue. Reviewed By: shergin Differential Revision: D21648905 fbshipit-source-id: 90ae65edb8a9276eecfea9770f554d8c56804797 * Introduce BatchedEventQueue::enqueueUniqueEvent Summary: Changelog: [Internal] `BatchedEventQueue::enqueueUniqueEvent` goes over event queue and deletes previous event of the same type and same target. This is useful for ScrollView for example where only the latest event is relevant. This only affects ScrollView scroll event, other events take the original code path. Reviewed By: mdvacca Differential Revision: D21648906 fbshipit-source-id: a80ad652058fd50ebb55e24a87229cdc1764b591 * Touchable: Revert `minPressDuration` on Legacy Components Summary: When `minPressDuration` was introduced to `Pressability`, all of the legacy Touchable components inherited the new default. This restore the former behavior for these legacy components so that only `Pressable` gets the new `minPressDuration` default value. Changelog: [General][Fixed] - Revert `minPressDuration` effect on legacy Touchable components Reviewed By: fkgozali Differential Revision: D21682764 fbshipit-source-id: b71a61843fae7f0f726155876a064fabd3ba1c64 * update internal code attribution Summary: Internal code attribution labeling update. Changelog: [Internal] Reviewed By: zlern2k Differential Revision: D21696075 fbshipit-source-id: ef689a6367e1dddfffbbefb52a6aead2c91bfefe * Fix imports in `RCTUtilsUIOverride.h` (#28946) Summary: While we build react native 0.62.2 via our Bazel build system, encountered those following errors due to lack of appropriate imports: ``` external/React-Core/React/Base/RCTUtilsUIOverride.h:8:33: error: cannot find interface declaration for 'NSObject', superclass of 'RCTUtilsUIOverride' interface RCTUtilsUIOverride : NSObject ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ external/React-Core/React/Base/RCTUtilsUIOverride.h:12:37: error: expected a type + (void)setPresentedViewController:(UIViewController *)presentedViewController; ^ ``` Add the appropriate imports `` and `` fix those errors. Honestly I dont know how it's supposed to work without those imports. Also all the siblings files have the correct imports. e.g. [RCTUtils.h](https://github.com/discord/react-native/blob/15a5f3624c40624d8dd0307bbcc1f2b2aba15a1b/React/Base/RCTUtils.h) ## Changelog [iOS] [Fixed] - Fix imports in `RCTUtilsUIOverride.h` Pull Request resolved: https://github.com/facebook/react-native/pull/28946 Test Plan: RN tester iOS app runs fine. Differential Revision: D21700030 Pulled By: shergin fbshipit-source-id: 9ef806b8f656bdad289fbdd3d84ecefb0dea6afb * Refactor types of ART Text class Summary: This diff refactors the types of ART Text classes, this is necessary on the next diffs of the stack closeoncommit changelog: [internal] Reviewed By: JoshuaGross Differential Revision: D21681876 fbshipit-source-id: ea438e89df6d860b3ff8bbdae657ca123b417a1b * Serialize ART text components and send data to Android Summary: This diff implements the serialization of Text components to send data from C++ to java changelog: [Internal] internal changes to support ART in fabric Reviewed By: JoshuaGross Differential Revision: D21681875 fbshipit-source-id: eba31f35c95e0a2d3226ec70421832719083d7fa * Add support for Text in ART Fabric Android Summary: This diff adds support for Text in ART Fabric Android changelog: [Internal] internal changes to fully support ART in Fabric Reviewed By: JoshuaGross Differential Revision: D21681877 fbshipit-source-id: c92e642cff56b71f8ee8f4eb9af6eea6c490f6c7 * Rename Element -> ARTElement Summary: Element, Shape, Group, Text are too generic, we are renaming these classes to ARTElement, ARTBGroup, ARTShape... changelog: [Internal] internal changes to support ART in android Reviewed By: JoshuaGross Differential Revision: D21681878 fbshipit-source-id: f6b35443486a3db210f61dcaf91bd32df47fbc66 * Implement equality of ARTElements and use it in ARTState Summary: Here I'm implementing equality methods for ARTGroup, ARTShape and ARTText and I'm using these methods to update the state only when it is necessary. This will improve perf in rendering of ART changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21695127 fbshipit-source-id: b438ddea4c34bd7a0bdf26a6aac4fd62a9f78b49 * Delete unused class Summary: ez diff to delete unused class changelog: [internal] Reviewed By: fkgozali Differential Revision: D21699666 fbshipit-source-id: d0e725c7096906e2effd16f8fa2a57683192420f * Add ART components into Catalyst app Android Summary: Simple diff to add ART components into Catalyst app Android changelog: [Internal] Internal changes to add support of ART for Fabric Reviewed By: JoshuaGross Differential Revision: D21621479 fbshipit-source-id: d957c25f447d19d8f349c69aa20f5f19237d867a * Delete copy constructor and copy assignment operator in ShadowNode Summary: Changelog: [Internal] ShadowNode shouldn't be copyable. Reviewed By: shergin Differential Revision: D21692756 fbshipit-source-id: e70dcf82f4d4c7609283936a42c741467f8f13ca * Annotate components in QPL logging using ImageAnalyticsTagContext Reviewed By: mdvacca Differential Revision: D21696266 fbshipit-source-id: b5c9d167e9da77ed969f7b4bdea1af9dd2e471ae * Cleanup of RCTTExtInputComponentView.mm Summary: Changelog: [Internal] 1. `UITextField.attributedText` and `UITextView.attributedText` default value is `nil`, not an empty NSAttributedString. 2. Assigning `_backedTextInputView.frame = self.bounds` inside constructor isn't needed as `self.bounds` during initialisation. Reviewed By: JoshuaGross Differential Revision: D21722661 fbshipit-source-id: 8725335d929b7d10736b540e12f1669ea824ad94 * Change jsi::Runtime::lockWeakObject to take a mutable ref Summary: A key difference in WeakRefs with the Hades GC is that they are mutable, in the sense that reading the value of a WeakRef while a GC is active might clear the WeakRef. For this reason, attempting to lock the WeakObject might mutate the backing reference. I preferred to communicate this change in behavior via the API rather than `const_cast` it away inside the implementation. Changelog: [Internal] Change jsi::WeakObject to be mutable in lockWeakObject Reviewed By: mhorowitz Differential Revision: D21485758 fbshipit-source-id: 3618928be8f8791aed56cb20673896ff5b786ded * Android Animated timing: interface-only Summary: This is (part of) a rewrite of D15390384. This implements the lifecycle interface only for Fabric to signal to NativeAnimatedModule when preOperations are about to run / operations are about to be dispatched. We will likely want to remove this mechanism entirely and rewrite NativeAnimatedModule in C++ for Fabric/Venice, but for now, I'm not sure of a better solution to unblock. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21698193 fbshipit-source-id: a13d445073911fd63d896202a7a1bfbe1167038a * Fix NativeAnimatedModule timing for Fabric/Venice(?) Summary: This is the second part of a rewrite of D15390384, which allows Animated timing to be driven by Paper or Fabric. The intuition is: we don't care which one drives the animation. We will expect one or both of them to issue a callback that operations are about to be executed, and the first one wins. The blocks will only execute once, the second time will be a noop. I don't think there's a 100% safe way of reimplementing Native Animated Module for Fabric/Venice (without a new API and implementing in C++) since it's inherently disconnected from the commit process and the tree. This gets us slightly closer to visual functionality, though. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21698192 fbshipit-source-id: c11d3cebd12cfc8acf4b63c87ccbe62cdbd8b672 * LayoutAnimations: fail silently instead of redboxing if there's a misconfigured LayoutAnimation Summary: property and type are optional params according to Flow, so we should treat as such. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21725746 fbshipit-source-id: 3c48a8cef8fa5911c195f582556de6dad871c4f1 * Split EventDispatcher into interface and impl (#28983) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/28983 Creating an interface for EventDispatcher so I can implement it from a dummy/no-op EventDispatcher to use in bridgeless mode when the instance isn't alive. Changelog: [Android] [Changed] Renamed EventDispatcher to EventDispatcherImpl and created EventDispatcher interface; calls to EventDispatcher contstructor need to be updated Reviewed By: makovkastar Differential Revision: D21695401 fbshipit-source-id: 46066a467efcf03a5f484bb9fb58c662d46a2c4e * C++ LayoutAnimations: solve crash when "animating" virtual views Summary: Index adjustment doesn't work if virtual views are inserted, because those don't actually result in the view hierarchy being mutated, as such. Add an Android-specific check to solve the crash. I don't love adding platform-specific checks here, so I'm considering wrapping this logic in a platform-specific class, so that logic lives outside of the core of LayoutAnimations and entirely in platform code. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21727805 fbshipit-source-id: 5af2cf479beaa4d0e9d94ea16ac989c4268920f8 * Add new swizzle method RCTSwapInstanceMethodWithBlock Summary: This diff adds a new swizzling method for replacing instance methods with blocks. Changelog: [Internal] Reviewed By: shergin Differential Revision: D21635131 fbshipit-source-id: c8061817bed66dad160efffee5a13c8714134540 * Remove unused double key press code Summary: This was added in D3343907 June 1st 2016, disabled in D3428043 June 16, 2016 and never re-enabled. Changelog: [Internal] Reviewed By: shergin Differential Revision: D21635227 fbshipit-source-id: db51dfb6271359bea7da34b4e2a71931fc7c2a63 * Migrate react-native Android tests to Robolectric v4 (#28927) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/28927 Changelog: [Android] [Changed] Test Modernization Reviewed By: mdvacca Differential Revision: D21598788 fbshipit-source-id: 6fd9c30220d6d69cc68f13bf1f5ad5a4bc2b039a * Better error message when crashing because of an invalid Remove operation Summary: Error messages like P131885276 don't help debugging, even when all mount instructions are logged to logcat. This log would help identify the incorrect mount instruction. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21735940 fbshipit-source-id: 16ff315c11ccafdd75d0ad9c7c60b7be2cd73202 * Move TimePickerAndroid to FB internal Summary: Moving TimePickerAndroid to FB internal. Changelog: [Android][Changed] - Moved TimePickerAndroid to FB internal. Reviewed By: cpojer Differential Revision: D21504128 fbshipit-source-id: 400c6ee7cff96a0d6b4205f7806ef8951b611b8c * Upgrade to Babel 7.10 Summary: See https://github.com/babel/babel/releases/tag/v7.10.0 for changes It includes a change I asked the Babel team for as they have been wrapping object spread calls with lots of extends call for spec compliance, but that only matters when you have side-effectful getters that affect the rest of the objects in the spread call and are order dependent. We should not be depending on such behavior in FB code so we can use loose mode and benefit from the app size wins. Changelog: [Internal] Reviewed By: motiz88 Differential Revision: D21738344 fbshipit-source-id: 5eeff47481e474bb41183119d990692b31751dd5 * Always return an EventDispatcher in bridgeless mode Summary: This changes how we access the EventDispatcher from the FabricUIManager in bridgeless mode. Currently, we have implemented a similar API to what we use for Fabric (used in UIManagerHelper): `BridgelessReactContext.getJSIModule(UIManager).getEventDispatcher()`. However, `getJSIModule` does not have a nullable return type, which means that we have to throw an exception if the UIManager can't be found - if, for example, the instance is not initialized yet (or has been destroyed). This is causing crashes when a view tries to access the EventDispatcher before the instance is initialized, which takes longer for Venice because we include JS bundle loading as part of initialization (we may need to revisit that). Ideally, we'd like to create a first-class API for `getEventDispatcher()`, and make sure that it never crashes if the instance is destroyed, because we don't care if JS events aren't delivered at that point. However, there are some obstacles to making this change for the bridge - mostly related to avoiding circular dependencies between the bridge module and the uimanager module. (Also, this might be a behavior change for the bridge, because I think we currently start queueing events before it's initialized? and product code might be relying on that). Reviewed By: mdvacca Differential Revision: D21672949 fbshipit-source-id: a38e96cd40c6f70124b7ca2a5c9722988fe7fcf4 * Reset RootView ID when root view is detached from view hierarchy Summary: I have a theory that T53114059 can be caused by (1) other crashes, and/or (2) reloads. Clear out the ID of a RootView when it is detached. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21732066 fbshipit-source-id: 5e607f34cf42ca26bdf199d5d3024fd49a60dc1e * Do not use variable types for array elements Summary: When a property returns an array type, use the actual element type when generating structs and inlines. Changelog: [Internal] Reviewed By: TheSavior Differential Revision: D21651351 fbshipit-source-id: 14cadf209c38a301c9c65fcaadd8a292c1936349 * Fix crash introduced by D21735940 in T67525923 Summary: We cannot call `parentView.getChildCount()` directly, we must get the child count through the ViewManager: A simple `Log.e` call shows: ``` MountingManager: parentView.getChildCount(): 0 // viewGroupManager.getChildCount(parentView): 7 ``` This difference does not occur for ALL views, but it occurs for... enough that this will crash on basically ~every screen, at least on navigation when all views are removed. Theory about why this is happening: some ViewGroup types compute childCount differently, especially if we do some sort of custom child management for some view type? By coercing to `T` which the ViewManager does, we call getChildCount on the correct class type. This is just a hypothesis, though. But the failing views are all `View`s and it does look like `ReactClippingViewManager` has custom `getChildCount` logic, so that's likely the answer. There's no such thing as an easy diff! Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21750097 fbshipit-source-id: 3d87d8f629a0c12101658050e57e09242dfc2a8c * Fix crash in removeViewAt by fixing incorrect error-detection Summary: As a followup to D21750097 and D21735940, it seems that ANY uses of childCount will be incorrect as they are "often" reported, incorrectly, as just 0. This is likely due to exotic cases like (1) the ViewManager childCount being overridden, or (2) special ViewManagers like BottomSheet where the childCount may actually be 1/0 even though many children are inserted. Anyway, as a more generic fix, let's only rethrow an existing Exception with additional diagnostics instead of trying to detect when this /would/ crash. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21756178 fbshipit-source-id: 17ffb2ed531978bae8d4db19d7b87ec62397e44b * Back out "Reset RootView ID when root view is detached from view hierarchy" Summary: Original commit changeset: 5e607f34cf42 Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D21757847 fbshipit-source-id: 96caf39f2ee99f64c96ebac9836ca178a7513cf1 * Refactor RCTKeyCommands, allow hotkeys to be used without command key Summary: This diff updates our RCTKeyCommands code to be more resilient by copying the [FLEX strategy for key commands](https://github.com/Flipboard/FLEX/blob/master/Classes/Utility/Keyboard/FLEXKeyboardShortcutManager.m). This strategy swizzles UIApplication handleKeyUIEvent which is further upstream than our UIResponder. It also allows for single key hotkeys like pressing just `r` instead of `cmd+r`. It does this without interfering with typing input by checking the first responder first. I've also updated our hotkey handling to support using just the keys like `r` in addition to `cmd+r`. In addition to brining these hotkeys more in line with other iOS tools, they're also easier to use and do not suffer the same issues hotkeys with modifiers like `cmd` have where keys are dropped. Changelog: [iOS] [Added] Allow hotkeys to be used without command key Reviewed By: shergin Differential Revision: D21635129 fbshipit-source-id: 36e0210a62b1f310473e152e8305165024cd338b * Remove calls to AllocationLocationTracker for native memory Summary: `getStackTracesTreeNodeForAlloc` only works for memory that was tracked with `newAlloc`. For now, that is only memory that goes through GC APIs. Various places were calling this function on native memory which was allocated with `malloc`, not GC memory. This led to SIGSEGV on nullptr when trying to take heap profiles of these objects. `newAlloc` could, in theory, work with native memory as well. But building out support for that is outside the scope of this fix. Changelog: [Internal] Reviewed By: jbower-fb Differential Revision: D21667842 fbshipit-source-id: 8403a6668e5ec607972ce6819f78fedb89da3f37 * Add Fabric logs to TextLayoutManager Summary: This diff add logging on Text Layout Manager. changelog: [Internal][Android] Internal changes to extend logging Reviewed By: JoshuaGross Differential Revision: D21737356 fbshipit-source-id: f34091722ff95f83ca41c9f17c49d31ce6618c9a * Add new bundle loading strategy in FBiOS behind GK Reviewed By: PeteTheHeat Differential Revision: D21706223 fbshipit-source-id: 805628eed004e1469a4b1aa1e69fd2982e448b25 * Extend Text to support measurement of empty Texts Summary: This diff extends the measurement of Text components in order to support empty strings. This is required for parity with Paper. I created a follow up task to analyze support of empty string as part of the Text infrastructure of Fabric in the future. changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D21761171 fbshipit-source-id: d2aa074052b09732af5d35723f19014090fcabbf * Revert Virtual Text Press Bug workaround Summary: This diff reverts the workaround added as part of D21432793, now that D21432793 is released in production for FB4A / FBiOS changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D21772129 fbshipit-source-id: 3b39738cb8fc871ec85d87f347c7f0ef408ccc3e * Clean up Flow & lint errors Summary: Changelog: [Internal] Reviewed By: motiz88 Differential Revision: D21703338 fbshipit-source-id: f33cf6dfdf8884ae76500ec55f44411e34e8cb76 * Delete ViewPagerAndroid JS and BUCK targets Summary: After the experiment in D21198302 ships to 100% and that code is deleted, we no longer need ViewPagerAndroid anywhere in FB so it can be deleted. This is distinct from deleting the native side of the component, which resides in open-source. Changelog: [Internal] Differential Revision: D21211134 fbshipit-source-id: 610e09792b079d34acbfcec836fde58a0b3648a7 * Delete ReactViewPager Summary: All ReactViewPager functionality should now be possible by using a ScrollView. Changelog: [Internal] ReactViewPager has been deprecated on Android and was removed from core as part of LeanCore Reviewed By: mdvacca Differential Revision: D21751774 fbshipit-source-id: 292475b9ffe7788b745329d13fd88dc3b613819e * Update Podfile.lock * Replace platform check in RCTUtilsUIOverride.h with direct import of RCTUIKit.h * Add eager initialization of RCTDevMenu to macOS RNTester * Fix for Android Build Issue after RN64 Merge Co-authored-by: Christoph Nakazawa Co-authored-by: Kevin Gozali Co-authored-by: zhongwuzw Co-authored-by: Tim Yung Co-authored-by: Samuel Susla Co-authored-by: Mike Grabowski Co-authored-by: Joshua Gross Co-authored-by: Kacie Bawiec Co-authored-by: Nick Gerleman Co-authored-by: Janic Duplessis Co-authored-by: Rubén Norte Co-authored-by: Will Holen Co-authored-by: Mats Byrkjeland Co-authored-by: Héctor Ramos Co-authored-by: Emily Janzer Co-authored-by: Paige Sun Co-authored-by: Rick Hanlon Co-authored-by: Nat Mote Co-authored-by: Valentin Shergin Co-authored-by: David Vacca Co-authored-by: Nikita Lutsenko Co-authored-by: Yuanzhe Bian Co-authored-by: Pieter De Baets Co-authored-by: Eloy Durán Co-authored-by: João Vieira Co-authored-by: Enes Co-authored-by: empyrical Co-authored-by: Peter Argany Co-authored-by: William Candillon Co-authored-by: Manvir Singh Co-authored-by: Lulu Wu Co-authored-by: Moti Zilberman Co-authored-by: Kacper Wiszczuk Co-authored-by: Vitaly Potlov Co-authored-by: Martin Sherburn Co-authored-by: Eli White Co-authored-by: Ramanpreet Nara Co-authored-by: Sidharth Guglani Co-authored-by: Keegan Mendonca Co-authored-by: Aditya Kumar Co-authored-by: Ram N Co-authored-by: duan Co-authored-by: Daniel Weaver Co-authored-by: Vojtech Novak Co-authored-by: lukewalczak Co-authored-by: Mike Vitousek Co-authored-by: Daniel Sainati Co-authored-by: Joshua Selbo Co-authored-by: Sakamoto, Kazunori Co-authored-by: generatedunixname89002005287564 Co-authored-by: Jimmy Zhang Co-authored-by: Ishan Khot Co-authored-by: Fanghao Co-authored-by: Christoph Purrer Co-authored-by: Jacek Burys Co-authored-by: Riley Dulin Co-authored-by: Mayuresh Gharpure --- .flowconfig | 6 +- .flowconfig.android | 6 +- .flowconfig.macos | 2 +- Libraries/Animated/src/Animated.js | 3 +- .../AndroidTextInputNativeComponent.js | 1 - Libraries/Components/TextInput/TextInput.js | 1 - Libraries/Components/Touchable/Touchable.js | 2 +- Libraries/Core/Devtools/getDevServer.js | 9 +- Libraries/Core/ExceptionsManager.js | 2 +- .../DeprecatedTextInputPropTypes.js | 1 - Libraries/FBLazyVector/BUCK | 2 +- Libraries/FBReactNativeSpec/BUCK | 2 +- .../FBReactNativeSpec-generated.mm | 40 - .../FBReactNativeSpec/FBReactNativeSpec.h | 93 -- Libraries/Image/Image.ios.js | 24 +- Libraries/Image/NativeImageEditor.js | 6 +- Libraries/Image/NativeImageStore.js | 5 +- Libraries/Image/RCTImageLoader.mm | 16 +- .../RCTImageLoaderWithAttributionProtocol.h | 5 + .../Image/RCTImageURLLoaderWithAttribution.h | 6 + Libraries/Image/RCTImageView.h | 1 + Libraries/Image/RCTImageView.mm | 30 +- Libraries/Image/RCTImageViewManager.mm | 1 + .../__snapshots__/Image-test.js.snap | 1 + Libraries/Pressability/Pressability.js | 14 +- Libraries/RCTRequired/BUCK | 2 +- Libraries/ReactNative/DummyUIManager.js | 1 - Libraries/ReactNative/NativeUIManager.js | 1 - Libraries/ReactNative/UIManagerProperties.js | 1 - .../RCTBackedTextInputViewProtocol.h | 1 + .../Text/TextInput/RCTBaseTextInputView.h | 1 + .../Text/TextInput/RCTBaseTextInputView.m | 12 + .../TextInput/RCTBaseTextInputViewManager.m | 1 + .../NativeTimePickerAndroid.js | 31 - Libraries/Utilities/HMRClient.js | 10 +- Libraries/Utilities/LoadingView.ios.js | 41 +- Libraries/Utilities/MatrixMath.js | 4 - Libraries/Utilities/ReactNativeTestTools.js | 20 +- RNTester/Podfile.lock | 81 +- RNTester/README.md | 4 +- RNTester/RNTester-macOS/AppDelegate.mm | 15 +- RNTester/RNTester/AppDelegate.mm | 9 + .../RNTesterPods.xcodeproj/project.pbxproj | 1 + .../RCTBundleURLProviderTests.m | 4 +- RNTester/js/components/TextInlineView.js | 16 + .../examples/ScrollView/ScrollViewExample.js | 32 + .../SegmentedControlExampleComponents.js | 114 +++ .../SegmentedControlIOSExample.js | 136 +-- .../js/examples/Text/TextExample.android.js | 3 + .../TextInput/TextInputExample.ios.js | 12 + React-Core.podspec | 1 + React/Base/RCTBundleURLProvider.h | 2 - React/Base/RCTBundleURLProvider.m | 9 + React/Base/RCTJSInvokerModule.h | 2 +- React/Base/RCTKeyCommands.h | 23 +- React/Base/RCTKeyCommands.m | 256 ++--- React/Base/RCTModuleData.mm | 70 +- React/Base/RCTUtils.h | 1 + React/Base/RCTUtils.m | 13 + React/Base/RCTUtilsUIOverride.h | 3 + React/CoreModules/BUCK | 3 +- React/CoreModules/RCTAppearance.h | 5 + React/CoreModules/RCTAppearance.mm | 4 +- React/CoreModules/RCTDevLoadingView.mm | 80 +- React/CoreModules/RCTExceptionsManager.mm | 25 +- React/CoreModules/RCTLogBox.h | 11 +- React/CoreModules/RCTLogBox.mm | 14 +- React/CxxBridge/RCTCxxBridge.mm | 38 +- React/CxxModule/RCTNativeModule.h | 1 + React/CxxModule/RCTNativeModule.mm | 99 +- ...CTLegacyViewManagerInteropComponentView.mm | 18 +- .../RCTFabricComponentsPlugins.h | 3 + .../RCTFabricComponentsPlugins.mm | 3 + .../RCTSafeAreaViewComponentView.mm | 11 +- .../ScrollView/RCTEnhancedScrollView.mm | 24 + .../ScrollView/RCTScrollViewComponentView.mm | 7 +- .../Text/RCTParagraphComponentView.mm | 6 + .../TextInput/RCTTextInputComponentView.mm | 15 +- .../View/RCTViewComponentView.mm | 13 +- .../Mounting/RCTComponentViewRegistry.mm | 2 +- React/Fabric/Mounting/RCTMountingManager.h | 2 - React/Fabric/Mounting/RCTMountingManager.mm | 7 +- React/Fabric/RCTScheduler.h | 6 + React/Fabric/RCTScheduler.mm | 85 +- React/Fabric/RCTSurfacePresenter.mm | 41 +- React/Fabric/RCTSurfaceTouchHandler.mm | 2 +- React/Fabric/Utils/PlatformRunLoopObserver.h | 54 + React/Fabric/Utils/PlatformRunLoopObserver.mm | 97 ++ React/Views/RCTConvert+Transform.m | 16 +- ReactAndroid/build.gradle | 10 +- ReactAndroid/gradle.properties | 6 +- .../com/facebook/react/testing/network/BUCK | 2 +- .../java/com/facebook/react/tests/BUCK | 1 - .../react/tests/TimePickerDialogTestCase.java | 156 --- .../main/java/com/facebook/debug/holder/BUCK | 2 +- .../main/java/com/facebook/debug/tags/BUCK | 2 +- .../main/java/com/facebook/fbreact/specs/BUCK | 4 +- .../specs/NativeTimePickerAndroidSpec.java | 30 - .../fbreact/specs/NativeUIManagerSpec.java | 3 - .../specs/jni/FBReactNativeSpec-generated.cpp | 27 - .../fbreact/specs/jni/FBReactNativeSpec.h | 14 - .../com/facebook/hermes/instrumentation/BUCK | 4 +- .../src/main/java/com/facebook/react/BUCK | 2 +- .../facebook/react/ReactInstanceManager.java | 50 +- .../java/com/facebook/react/animated/BUCK | 2 +- .../react/animated/NativeAnimatedModule.java | 93 +- .../main/java/com/facebook/react/bridge/BUCK | 4 +- .../com/facebook/react/bridge/UIManager.java | 15 + .../react/bridge/UIManagerListener.java | 21 + .../main/java/com/facebook/react/common/BUCK | 2 +- .../com/facebook/react/common/network/BUCK | 2 +- .../main/java/com/facebook/react/config/BUCK | 2 +- .../react/config/ReactFeatureFlags.java | 13 + .../java/com/facebook/react/devsupport/BUCK | 4 +- .../react/devsupport/DevServerHelper.java | 5 +- .../main/java/com/facebook/react/fabric/BUCK | 2 +- .../com/facebook/react/fabric/Binding.java | 2 + .../react/fabric/FabricJSIModuleProvider.java | 2 - .../react/fabric/FabricUIManager.java | 73 +- .../java/com/facebook/react/fabric/jni/BUCK | 3 +- .../com/facebook/react/fabric/jni/Binding.cpp | 93 +- .../com/facebook/react/fabric/jni/Binding.h | 30 +- .../fabric/mounting/MountingManager.java | 74 +- .../mountitems/SendAccessibilityEvent.java | 17 +- .../mountitems/UpdateLocalDataMountItem.java | 46 - .../java/com/facebook/react/jscexecutor/BUCK | 2 +- .../main/java/com/facebook/react/jstasks/BUCK | 2 +- .../facebook/react/module/annotations/BUCK | 2 +- .../java/com/facebook/react/module/model/BUCK | 2 +- .../com/facebook/react/module/processing/BUCK | 2 +- .../react/modules/accessibilityinfo/BUCK | 2 +- .../facebook/react/modules/appearance/BUCK | 2 +- .../facebook/react/modules/appregistry/BUCK | 2 +- .../com/facebook/react/modules/appstate/BUCK | 2 +- .../java/com/facebook/react/modules/blob/BUCK | 2 +- .../com/facebook/react/modules/blob/jni/BUCK | 2 +- .../com/facebook/react/modules/camera/BUCK | 2 +- .../com/facebook/react/modules/clipboard/BUCK | 2 +- .../com/facebook/react/modules/common/BUCK | 2 +- .../java/com/facebook/react/modules/core/BUCK | 2 +- .../facebook/react/modules/datepicker/BUCK | 2 +- .../com/facebook/react/modules/debug/BUCK | 4 +- .../facebook/react/modules/deviceinfo/BUCK | 2 +- .../com/facebook/react/modules/dialog/BUCK | 2 +- .../com/facebook/react/modules/fabric/BUCK | 2 +- .../com/facebook/react/modules/fresco/BUCK | 2 +- .../facebook/react/modules/i18nmanager/BUCK | 2 +- .../com/facebook/react/modules/image/BUCK | 2 +- .../com/facebook/react/modules/intent/BUCK | 2 +- .../com/facebook/react/modules/network/BUCK | 2 +- .../facebook/react/modules/permissions/BUCK | 2 +- .../com/facebook/react/modules/share/BUCK | 2 +- .../com/facebook/react/modules/sound/BUCK | 2 +- .../com/facebook/react/modules/statusbar/BUCK | 2 +- .../com/facebook/react/modules/storage/BUCK | 2 +- .../facebook/react/modules/systeminfo/BUCK | 4 +- .../facebook/react/modules/timepicker/BUCK | 26 - .../DismissableTimePickerDialog.java | 50 - .../timepicker/TimePickerDialogFragment.java | 100 -- .../timepicker/TimePickerDialogModule.java | 136 --- .../com/facebook/react/modules/toast/BUCK | 2 +- .../com/facebook/react/modules/vibration/BUCK | 2 +- .../com/facebook/react/modules/websocket/BUCK | 2 +- .../facebook/react/packagerconnection/BUCK | 2 +- .../java/com/facebook/react/processing/BUCK | 2 +- .../main/java/com/facebook/react/shell/BUCK | 4 +- .../react/shell/MainReactPackage.java | 7 - .../main/java/com/facebook/react/surface/BUCK | 2 +- .../main/java/com/facebook/react/touch/BUCK | 2 +- .../com/facebook/react/turbomodule/core/BUCK | 4 +- .../react/turbomodule/core/interfaces/BUCK | 2 +- .../facebook/react/turbomodule/core/jni/BUCK | 4 +- .../java/com/facebook/react/uimanager/BUCK | 4 +- .../uimanager/BaseViewManagerDelegate.java | 3 + .../react/uimanager/MatrixMathHelper.java | 4 - .../uimanager/NativeViewHierarchyManager.java | 9 +- .../react/uimanager/ReactShadowNode.java | 2 + .../react/uimanager/ReactShadowNodeImpl.java | 5 + .../react/uimanager/ThemedReactContext.java | 14 +- .../react/uimanager/TransformHelper.java | 11 + .../react/uimanager/UIManagerHelper.java | 50 +- .../react/uimanager/UIManagerModule.java | 46 +- .../uimanager/UIManagerModuleListener.java | 6 +- .../facebook/react/uimanager/ViewManager.java | 7 +- .../react/uimanager/ViewManagerDelegate.java | 6 +- .../facebook/react/uimanager/annotations/BUCK | 2 +- .../com/facebook/react/uimanager/common/BUCK | 2 +- .../events/BlackHoleEventDispatcher.java | 57 ++ .../uimanager/events/EventDispatcher.java | 375 +------ .../uimanager/events/EventDispatcherImpl.java | 389 ++++++++ .../events/EventDispatcherProvider.java | 24 + .../com/facebook/react/uimanager/util/BUCK | 2 +- .../main/java/com/facebook/react/util/BUCK | 2 +- .../AndroidDrawerLayoutManagerDelegate.java | 7 +- ...roidSwipeRefreshLayoutManagerDelegate.java | 5 +- .../AndroidSwitchManagerDelegate.java | 5 +- .../AndroidViewPagerManagerDelegate.java | 7 +- .../java/com/facebook/react/viewmanagers/BUCK | 2 +- .../viewmanagers/SwitchManagerDelegate.java | 6 +- .../java/com/facebook/react/views/common/BUCK | 2 +- .../java/com/facebook/react/views/drawer/BUCK | 2 +- .../java/com/facebook/react/views/image/BUCK | 4 +- .../com/facebook/react/views/imagehelper/BUCK | 4 +- .../java/com/facebook/react/views/modal/BUCK | 2 +- .../java/com/facebook/react/views/picker/BUCK | 2 +- .../com/facebook/react/views/progressbar/BUCK | 2 +- .../java/com/facebook/react/views/scroll/BUCK | 2 +- .../scroll/ReactHorizontalScrollView.java | 57 +- .../react/views/scroll/ReactScrollView.java | 57 +- .../java/com/facebook/react/views/slider/BUCK | 2 +- .../views/slider/ReactSliderManager.java | 2 +- .../facebook/react/views/swiperefresh/BUCK | 2 +- .../SwipeRefreshLayoutManager.java | 2 +- .../com/facebook/react/views/switchview/BUCK | 2 +- .../views/switchview/ReactSwitchManager.java | 4 +- .../java/com/facebook/react/views/text/BUCK | 2 +- .../views/text/ReactTextViewManager.java | 2 +- .../react/views/text/TextLayoutManager.java | 38 +- .../react/views/text/frescosupport/BUCK | 2 +- .../com/facebook/react/views/textinput/BUCK | 2 +- .../react/views/unimplementedview/BUCK | 2 +- .../java/com/facebook/react/views/view/BUCK | 2 +- .../react/views/view/ReactDrawableHelper.java | 23 +- .../react/views/view/ReactViewManager.java | 12 +- .../com/facebook/react/views/viewpager/BUCK | 31 - .../views/viewpager/PageScrollEvent.java | 59 -- .../PageScrollStateChangedEvent.java | 50 - .../views/viewpager/PageSelectedEvent.java | 50 - .../react/views/viewpager/ReactViewPager.java | 263 ----- .../viewpager/ReactViewPagerManager.java | 175 ---- .../first-party/yogajni/jni/YGJNIVanilla.cpp | 7 + .../src/main/jni/react/jni/Android.mk | 1 + ReactAndroid/src/main/jni/react/jni/BUCK | 2 +- .../main/jni/react/jni/JavaModuleWrapper.cpp | 22 + .../main/jni/react/jni/JavaModuleWrapper.h | 1 + .../src/main/jni/react/jni/MethodInvoker.cpp | 6 + .../src/main/jni/react/jni/MethodInvoker.h | 4 + .../src/test/java/com/facebook/powermock/BUCK | 166 +-- .../src/main/res/shell/values/styles.xml | 17 - .../main/third-party/android/androidx/BUCK | 20 + .../src/main/third-party/java/asm/BUCK | 20 +- .../src/main/third-party/java/mockito2/BUCK | 60 ++ .../third-party/java/robolectric/4.3.1/BUCK | 216 ++++ .../java/robolectric3/robolectric/BUCK | 180 ---- .../src/test/java/com/facebook/react/BUCK | 3 - .../java/com/facebook/react/animated/BUCK | 3 - .../test/java/com/facebook/react/bridge/BUCK | 9 +- .../react/bridge/BaseJavaModuleTest.java | 11 +- .../java/com/facebook/react/devsupport/BUCK | 3 - .../JSDebuggerWebSocketClientTest.java | 9 +- .../test/java/com/facebook/react/modules/BUCK | 3 - .../modules/dialog/DialogModuleTest.java | 2 +- .../modules/network/NetworkingModuleTest.java | 10 +- .../network/ReactCookieJarContainerTest.java | 12 +- .../react/modules/share/ShareModuleTest.java | 27 +- .../storage/AsyncStorageModuleTest.java | 2 +- .../facebook/react/packagerconnection/BUCK | 3 - .../java/com/facebook/react/uimanager/BUCK | 3 - .../react/uimanager/layoutanimation/BUCK | 1 - .../test/java/com/facebook/react/util/BUCK | 3 - .../test/java/com/facebook/react/views/BUCK | 3 - .../test/java/org/mockito/configuration/BUCK | 4 +- ReactCommon/ReactCommon.podspec | 1 + ReactCommon/better/BUCK | 3 +- ReactCommon/callinvoker/BUCK | 3 +- ReactCommon/config/BUCK | 3 +- ReactCommon/cxxreact/Android.mk | 3 +- ReactCommon/cxxreact/BUCK | 11 +- ReactCommon/cxxreact/CxxNativeModule.cpp | 12 + ReactCommon/cxxreact/CxxNativeModule.h | 1 + ReactCommon/cxxreact/ModuleRegistry.cpp | 54 +- ReactCommon/cxxreact/ModuleRegistry.h | 5 + ReactCommon/cxxreact/NativeModule.h | 1 + ReactCommon/cxxreact/NativeToJsBridge.cpp | 7 +- ReactCommon/cxxreact/React-cxxreact.podspec | 2 + ReactCommon/fabric/animations/BUCK | 96 ++ .../animations/LayoutAnimationDriver.cpp | 197 ++++ .../fabric/animations/LayoutAnimationDriver.h | 43 + .../LayoutAnimationKeyFrameManager.cpp | 944 ++++++++++++++++++ .../LayoutAnimationKeyFrameManager.h | 186 ++++ ReactCommon/fabric/attributedstring/BUCK | 3 +- .../fabric/attributedstring/conversions.h | 4 +- ReactCommon/fabric/componentregistry/BUCK | 3 +- .../fabric/components/activityindicator/BUCK | 3 +- .../fabric/components/art/ARTBaseShadowNode.h | 36 + ReactCommon/fabric/components/art/BUCK | 94 ++ .../fabric/components/art/conversions.h | 137 +++ .../art/group/ARTGroupComponentDescriptor.h | 20 + .../components/art/group/ARTGroupProps.cpp | 33 + .../components/art/group/ARTGroupProps.h | 37 + .../art/group/ARTGroupShadowNode.cpp | 32 + .../components/art/group/ARTGroupShadowNode.h | 36 + .../art/shape/ARTShapeComponentDescriptor.h | 20 + .../components/art/shape/ARTShapeProps.cpp | 49 + .../components/art/shape/ARTShapeProps.h | 46 + .../art/shape/ARTShapeShadowNode.cpp | 31 + .../components/art/shape/ARTShapeShadowNode.h | 35 + .../components/art/state/ARTElement.cpp | 10 +- .../fabric/components/art/state/ARTElement.h | 81 ++ .../fabric/components/art/state/ARTGroup.cpp | 40 + .../fabric/components/art/state/ARTGroup.h | 49 + .../fabric/components/art/state/ARTShape.cpp | 55 + .../fabric/components/art/state/ARTShape.h | 63 ++ .../fabric/components/art/state/ARTText.cpp | 59 ++ .../fabric/components/art/state/ARTText.h | 68 ++ .../fabric/components/art/state/primitives.h | 56 ++ .../ARTSurfaceViewComponentDescriptor.h | 20 + .../art/surfaceview/ARTSurfaceViewProps.cpp | 26 + .../art/surfaceview/ARTSurfaceViewProps.h | 31 + .../surfaceview/ARTSurfaceViewShadowNode.cpp | 47 + .../surfaceview/ARTSurfaceViewShadowNode.h | 62 ++ .../art/surfaceview/ARTSurfaceViewState.cpp | 22 + .../art/surfaceview/ARTSurfaceViewState.h | 40 + .../art/text/ARTTextComponentDescriptor.h | 20 + .../components/art/text/ARTTextProps.cpp | 49 + .../fabric/components/art/text/ARTTextProps.h | 110 ++ .../components/art/text/ARTTextShadowNode.cpp | 34 + .../components/art/text/ARTTextShadowNode.h | 34 + ReactCommon/fabric/components/image/BUCK | 3 +- .../components/image/ImageShadowNode.cpp | 17 +- .../components/legacyviewmanagerinterop/BUCK | 2 +- ...cyViewManagerInteropComponentDescriptor.mm | 4 + .../RCTLegacyViewManagerInteropCoordinator.mm | 2 +- ReactCommon/fabric/components/modal/BUCK | 3 +- ReactCommon/fabric/components/picker/BUCK | 3 +- ReactCommon/fabric/components/root/BUCK | 3 +- .../fabric/components/safeareaview/BUCK | 2 +- .../safeareaview/SafeAreaViewShadowNode.cpp | 8 + .../safeareaview/SafeAreaViewShadowNode.h | 4 + ReactCommon/fabric/components/scrollview/BUCK | 3 +- .../scrollview/ScrollViewEventEmitter.cpp | 4 +- .../components/scrollview/ScrollViewProps.cpp | 13 +- .../components/scrollview/ScrollViewProps.h | 53 +- ReactCommon/fabric/components/slider/BUCK | 3 +- ReactCommon/fabric/components/switch/BUCK | 3 +- ReactCommon/fabric/components/text/BUCK | 3 +- .../components/text/basetext/BaseTextProps.h | 2 +- .../text/basetext/BaseTextShadowNode.h | 7 + .../paragraph/ParagraphComponentDescriptor.h | 13 + .../text/paragraph/ParagraphProps.cpp | 9 +- .../text/paragraph/ParagraphShadowNode.cpp | 14 +- ReactCommon/fabric/components/textinput/BUCK | 3 +- .../AndroidTextInputShadowNode.cpp | 4 +- .../components/textinput/iostextinput/BUCK | 2 +- .../iostextinput/TextInputShadowNode.cpp | 6 +- .../textinput/iostextinput/primitives.h | 6 + .../textinput/iostextinput/propsConversions.h | 5 + .../fabric/components/unimplementedview/BUCK | 2 +- ReactCommon/fabric/components/view/BUCK | 3 +- .../components/view/ViewComponentDescriptor.h | 21 +- .../components/view/ViewPropsInterpolation.h | 58 ++ .../fabric/components/view/ViewShadowNode.cpp | 5 +- .../fabric/components/view/conversions.h | 2 +- .../view/yoga/YogaLayoutableShadowNode.cpp | 15 +- .../view/yoga/YogaLayoutableShadowNode.h | 10 + ReactCommon/fabric/core/BUCK | 3 +- .../componentdescriptor/ComponentDescriptor.h | 9 + .../ConcreteComponentDescriptor.h | 8 + .../fabric/core/events/BatchedEventQueue.cpp | 19 + .../fabric/core/events/BatchedEventQueue.h | 8 + .../fabric/core/events/EventDispatcher.cpp | 53 +- .../fabric/core/events/EventDispatcher.h | 15 +- .../fabric/core/events/EventEmitter.cpp | 14 + ReactCommon/fabric/core/events/EventEmitter.h | 5 + ReactCommon/fabric/core/events/EventQueue.cpp | 2 +- ReactCommon/fabric/core/events/RawEvent.h | 6 +- .../core/layout/LayoutableShadowNode.cpp | 140 +-- .../fabric/core/layout/LayoutableShadowNode.h | 19 + ReactCommon/fabric/core/primitives/RawValue.h | 7 +- ReactCommon/fabric/core/shadownode/Props.h | 2 +- .../fabric/core/shadownode/ShadowNode.cpp | 2 +- .../fabric/core/shadownode/ShadowNode.h | 6 + .../core/tests/LayoutableShadowNodeTest.cpp | 32 +- ReactCommon/fabric/debug/BUCK | 3 +- ReactCommon/fabric/element/BUCK | 2 +- ReactCommon/fabric/graphics/BUCK | 3 +- ReactCommon/fabric/graphics/Quaternion.h | 208 ++++ ReactCommon/fabric/graphics/Transform.cpp | 144 ++- ReactCommon/fabric/graphics/Transform.h | 84 ++ ReactCommon/fabric/imagemanager/BUCK | 3 +- ReactCommon/fabric/mapbuffer/BUCK | 2 +- ReactCommon/fabric/mounting/BUCK | 3 +- .../fabric/mounting/Differentiator.cpp | 228 +---- ReactCommon/fabric/mounting/Differentiator.h | 1 - .../fabric/mounting/MountingCoordinator.cpp | 104 +- .../fabric/mounting/MountingCoordinator.h | 29 +- .../mounting/MountingOverrideDelegate.h | 47 + .../fabric/mounting/MountingTransaction.h | 2 +- ReactCommon/fabric/mounting/ShadowTree.cpp | 15 +- ReactCommon/fabric/mounting/ShadowTree.h | 11 +- .../fabric/mounting/ShadowTreeRegistry.cpp | 3 +- .../fabric/mounting/ShadowTreeRevision.cpp | 4 + .../fabric/mounting/ShadowTreeRevision.h | 14 +- ReactCommon/fabric/mounting/ShadowView.cpp | 6 +- ReactCommon/fabric/mounting/ShadowView.h | 2 +- .../fabric/mounting/tests/MountingTest.cpp | 18 +- .../tests/ShadowTreeLifeCycleTest.cpp | 29 +- .../tests/StateReconciliationTest.cpp | 3 +- ReactCommon/fabric/scheduler/BUCK | 3 +- ReactCommon/fabric/scheduler/Scheduler.cpp | 60 +- ReactCommon/fabric/scheduler/Scheduler.h | 33 +- .../fabric/scheduler/SchedulerToolbox.h | 6 + ReactCommon/fabric/templateprocessor/BUCK | 3 +- ReactCommon/fabric/textlayoutmanager/BUCK | 3 +- .../platform/android/TextLayoutManager.cpp | 10 +- .../platform/cxx/TextLayoutManager.cpp | 4 +- .../platform/cxx/TextLayoutManager.h | 3 +- ReactCommon/fabric/uimanager/BUCK | 3 +- .../uimanager/LayoutAnimationStatusDelegate.h | 30 + ReactCommon/fabric/uimanager/UIManager.cpp | 71 +- ReactCommon/fabric/uimanager/UIManager.h | 17 +- .../uimanager/UIManagerAnimationDelegate.h | 45 + .../fabric/uimanager/UIManagerBinding.cpp | 8 +- .../fabric/uimanager/UIManagerBinding.h | 1 + ReactCommon/hermes/inspector/BUCK | 9 +- ReactCommon/hermes/inspector/Inspector.cpp | 57 +- ReactCommon/hermes/inspector/Inspector.h | 15 + .../hermes/inspector/InspectorState.cpp | 6 + ReactCommon/hermes/inspector/InspectorState.h | 4 +- .../hermes/inspector/chrome/Connection.cpp | 19 +- .../hermes/inspector/chrome/MessageTypes.cpp | 26 +- .../hermes/inspector/chrome/MessageTypes.h | 13 +- .../chrome/tests/ConnectionTests.cpp | 42 +- .../inspector/chrome/tests/SyncConnection.cpp | 7 +- .../inspector/chrome/tests/SyncConnection.h | 4 +- .../hermes/inspector/tools/message_types.txt | 1 + ReactCommon/jsi/BUCK | 4 +- ReactCommon/jsi/JSCRuntime.cpp | 4 +- ReactCommon/jsi/jsi/decorator.h | 4 +- ReactCommon/jsi/jsi/jsi-inl.h | 4 + ReactCommon/jsi/jsi/jsi.h | 3 +- ReactCommon/jsiexecutor/Android.mk | 2 +- ReactCommon/jsiexecutor/BUCK | 4 +- .../jsiexecutor/React-jsiexecutor.podspec | 1 + .../jsiexecutor/jsireact/JSIExecutor.cpp | 52 +- .../jsiexecutor/jsireact/JSIExecutor.h | 1 + .../jsiexecutor/jsireact/JSINativeModules.cpp | 14 +- ReactCommon/jsinspector/BUCK | 3 +- ReactCommon/microprofiler/BUCK | 3 +- ReactCommon/reactperflogger/.clang-tidy | 5 + ReactCommon/reactperflogger/Android.mk | 25 + ReactCommon/reactperflogger/BUCK | 28 + .../reactperflogger/React-perflogger.podspec | 34 + .../BridgeNativeModulePerfLogger.cpp | 320 ++++++ .../BridgeNativeModulePerfLogger.h | 111 ++ .../reactperflogger/NativeModulePerfLogger.h | 161 +++ ReactCommon/runtimeexecutor/BUCK | 2 +- .../ReactCommon/RuntimeExecutor.h | 12 +- ReactCommon/turbomodule/core/BUCK | 5 +- .../core/TurboModulePerfLogger.cpp | 320 ++++++ .../turbomodule/core/TurboModulePerfLogger.h | 110 ++ .../core/platform/ios/RCTTurboModule.h | 118 +-- .../core/platform/ios/RCTTurboModule.mm | 151 ++- .../core/platform/ios/RCTTurboModuleManager.h | 9 +- .../platform/ios/RCTTurboModuleManager.mm | 184 ++-- ReactCommon/turbomodule/samples/BUCK | 3 +- ReactCommon/utils/BUCK | 3 +- ReactCommon/yoga/yoga/Utils.cpp | 4 + ReactCommon/yoga/yoga/Utils.h | 2 + ReactCommon/yoga/yoga/Yoga.cpp | 6 +- ReactCommon/yoga/yoga/log.cpp | 4 - .../react/views/view/ReactViewManager.java | 24 +- .../facebook/react/ReactInstanceManager.java | 22 +- .../src/main/jni/react/jni/Android.mk | 13 +- jest/renderer.js | 4 +- package.json | 3 +- .../README.md | 2 +- packages/react-native-codegen/DEFS.bzl | 1 - .../components/GeneratePropsJavaDelegate.js | 5 +- .../GeneratePropsJavaDelegate-test.js.snap | 14 +- .../modules/ObjCppUtils/GenerateStructs.js | 141 ++- .../modules/__test_fixtures__/fixtures.js | 81 ++ .../GenerateModuleCpp-test.js.snap | 6 + .../GenerateModuleH-test.js.snap | 1 + .../GenerateModuleHObjCpp-test.js.snap | 63 +- .../GenerateModuleMm-test.js.snap | 15 + .../src/parsers/flow/modules/methods.js | 1 + scripts/generate-native-modules-specs-cli.js | 78 ++ scripts/generate-native-modules-specs.sh | 27 +- scripts/react_native_pods.rb | 1 + template.config.js | 1 + template/_flowconfig | 6 +- template/ios/HelloWorld/Info.plist | 2 - tools/build_defs/oss/rn_defs.bzl | 13 +- yarn.lock | 316 +++--- 485 files changed, 9528 insertions(+), 4217 deletions(-) delete mode 100644 Libraries/TimePickerAndroid/NativeTimePickerAndroid.js create mode 100644 RNTester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js create mode 100644 React/Fabric/Utils/PlatformRunLoopObserver.h create mode 100644 React/Fabric/Utils/PlatformRunLoopObserver.mm delete mode 100644 ReactAndroid/src/androidTest/java/com/facebook/react/tests/TimePickerDialogTestCase.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeTimePickerAndroidSpec.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLocalDataMountItem.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/BUCK delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/DismissableTimePickerDialog.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogFragment.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogModule.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java create mode 100644 ReactAndroid/src/main/third-party/java/mockito2/BUCK create mode 100644 ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK delete mode 100644 ReactAndroid/src/main/third-party/java/robolectric3/robolectric/BUCK create mode 100644 ReactCommon/fabric/animations/BUCK create mode 100644 ReactCommon/fabric/animations/LayoutAnimationDriver.cpp create mode 100644 ReactCommon/fabric/animations/LayoutAnimationDriver.h create mode 100644 ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp create mode 100644 ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h create mode 100644 ReactCommon/fabric/components/art/ARTBaseShadowNode.h create mode 100644 ReactCommon/fabric/components/art/BUCK create mode 100644 ReactCommon/fabric/components/art/conversions.h create mode 100644 ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h create mode 100644 ReactCommon/fabric/components/art/group/ARTGroupProps.cpp create mode 100644 ReactCommon/fabric/components/art/group/ARTGroupProps.h create mode 100644 ReactCommon/fabric/components/art/group/ARTGroupShadowNode.cpp create mode 100644 ReactCommon/fabric/components/art/group/ARTGroupShadowNode.h create mode 100644 ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h create mode 100644 ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp create mode 100644 ReactCommon/fabric/components/art/shape/ARTShapeProps.h create mode 100644 ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp create mode 100644 ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h rename ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerMode.java => ReactCommon/fabric/components/art/state/ARTElement.cpp (59%) create mode 100644 ReactCommon/fabric/components/art/state/ARTElement.h create mode 100644 ReactCommon/fabric/components/art/state/ARTGroup.cpp create mode 100644 ReactCommon/fabric/components/art/state/ARTGroup.h create mode 100644 ReactCommon/fabric/components/art/state/ARTShape.cpp create mode 100644 ReactCommon/fabric/components/art/state/ARTShape.h create mode 100644 ReactCommon/fabric/components/art/state/ARTText.cpp create mode 100644 ReactCommon/fabric/components/art/state/ARTText.h create mode 100644 ReactCommon/fabric/components/art/state/primitives.h create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewComponentDescriptor.h create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.cpp create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.h create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.cpp create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.h create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.cpp create mode 100644 ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.h create mode 100644 ReactCommon/fabric/components/art/text/ARTTextComponentDescriptor.h create mode 100644 ReactCommon/fabric/components/art/text/ARTTextProps.cpp create mode 100644 ReactCommon/fabric/components/art/text/ARTTextProps.h create mode 100644 ReactCommon/fabric/components/art/text/ARTTextShadowNode.cpp create mode 100644 ReactCommon/fabric/components/art/text/ARTTextShadowNode.h create mode 100644 ReactCommon/fabric/components/view/ViewPropsInterpolation.h create mode 100644 ReactCommon/fabric/graphics/Quaternion.h create mode 100644 ReactCommon/fabric/mounting/MountingOverrideDelegate.h create mode 100644 ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h create mode 100644 ReactCommon/fabric/uimanager/UIManagerAnimationDelegate.h create mode 100644 ReactCommon/reactperflogger/.clang-tidy create mode 100644 ReactCommon/reactperflogger/Android.mk create mode 100644 ReactCommon/reactperflogger/BUCK create mode 100644 ReactCommon/reactperflogger/React-perflogger.podspec create mode 100644 ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.cpp create mode 100644 ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.h create mode 100644 ReactCommon/reactperflogger/reactperflogger/NativeModulePerfLogger.h create mode 100644 ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp create mode 100644 ReactCommon/turbomodule/core/TurboModulePerfLogger.h create mode 100644 scripts/generate-native-modules-specs-cli.js diff --git a/.flowconfig b/.flowconfig index 4af17e66aefe84..2baa79d4e4fd00 100644 --- a/.flowconfig +++ b/.flowconfig @@ -59,10 +59,6 @@ suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState suppress_type=$FlowFixMeEmpty -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError - experimental.well_formed_exports=true experimental.types_first=true experimental.abstract_locations=true @@ -90,4 +86,4 @@ untyped-import untyped-type-import [version] -^0.124.0 +^0.125.0 diff --git a/.flowconfig.android b/.flowconfig.android index db4bff3b3cfbc9..0e766ae9d8ffd4 100644 --- a/.flowconfig.android +++ b/.flowconfig.android @@ -62,10 +62,6 @@ suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState suppress_type=$FlowFixMeEmpty -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_android\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_android\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError - experimental.well_formed_exports=true experimental.types_first=true experimental.abstract_locations=true @@ -93,4 +89,4 @@ untyped-import untyped-type-import [version] -^0.124.0 +^0.125.0 diff --git a/.flowconfig.macos b/.flowconfig.macos index 9cf5a7d0dcb5bd..e03d7fb76c0e5d 100644 --- a/.flowconfig.macos +++ b/.flowconfig.macos @@ -89,4 +89,4 @@ untyped-import untyped-type-import [version] -^0.124.0 +^0.125.0 diff --git a/Libraries/Animated/src/Animated.js b/Libraries/Animated/src/Animated.js index 0891d75a3f3649..53dd63c4709566 100644 --- a/Libraries/Animated/src/Animated.js +++ b/Libraries/Animated/src/Animated.js @@ -21,7 +21,8 @@ import typeof AnimatedView from './components/AnimatedView'; const AnimatedMock = require('./AnimatedMock'); const AnimatedImplementation = require('./AnimatedImplementation'); -const Animated = ((Platform.isTesting +const Animated = ((Platform.isTesting || +(Platform.OS === 'android' && global.RN$Bridgeless) ? AnimatedMock : AnimatedImplementation): typeof AnimatedMock); diff --git a/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js index 61e37c54bea434..27f7abbe7d1c2f 100644 --- a/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js +++ b/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js @@ -183,7 +183,6 @@ export type NativeProps = $ReadOnly<{| /** * When `false`, it will prevent the soft keyboard from showing when the field is focused. * Defaults to `true`. - * @platform android */ showSoftInputOnFocus?: ?boolean, diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index dd82eaf0a7027f..034ce9b70031e8 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -423,7 +423,6 @@ type AndroidProps = $ReadOnly<{| /** * When `false`, it will prevent the soft keyboard from showing when the field is focused. * Defaults to `true`. - * @platform android */ showSoftInputOnFocus?: ?boolean, |}>; diff --git a/Libraries/Components/Touchable/Touchable.js b/Libraries/Components/Touchable/Touchable.js index c9c8532867ec03..a55164413f4bba 100644 --- a/Libraries/Components/Touchable/Touchable.js +++ b/Libraries/Components/Touchable/Touchable.js @@ -701,7 +701,7 @@ const TouchableMixin = { globalY: number, ) { //don't do anything UIManager failed to measure node - if (!(l > 0 || t > 0 || w > 0 || h > 0 || globalX > 0 || globalY > 0)) { + if (!l && !t && !w && !h && !globalX && !globalY) { return; } this.state.touchable.positionOnActivate && diff --git a/Libraries/Core/Devtools/getDevServer.js b/Libraries/Core/Devtools/getDevServer.js index 5f8dfe1fcc26dc..c60d0e51606035 100644 --- a/Libraries/Core/Devtools/getDevServer.js +++ b/Libraries/Core/Devtools/getDevServer.js @@ -13,10 +13,12 @@ import NativeSourceCode from '../../NativeModules/specs/NativeSourceCode'; let _cachedDevServerURL: ?string; +let _cachedFullBundleURL: ?string; const FALLBACK = 'http://localhost:8081/'; type DevServerInfo = { url: string, + fullBundleUrl: ?string, bundleLoadedFromServer: boolean, ... }; @@ -27,14 +29,15 @@ type DevServerInfo = { */ function getDevServer(): DevServerInfo { if (_cachedDevServerURL === undefined) { - const match = NativeSourceCode.getConstants().scriptURL.match( - /^https?:\/\/.*?\//, - ); + const scriptUrl = NativeSourceCode.getConstants().scriptURL; + const match = scriptUrl.match(/^https?:\/\/.*?\//); _cachedDevServerURL = match ? match[0] : null; + _cachedFullBundleURL = match ? scriptUrl : null; } return { url: _cachedDevServerURL || FALLBACK, + fullBundleUrl: _cachedFullBundleURL, bundleLoadedFromServer: _cachedDevServerURL !== null, }; } diff --git a/Libraries/Core/ExceptionsManager.js b/Libraries/Core/ExceptionsManager.js index fd6ce28ad6e732..a151d26c5d5ff1 100644 --- a/Libraries/Core/ExceptionsManager.js +++ b/Libraries/Core/ExceptionsManager.js @@ -76,7 +76,7 @@ function reportException( message = e.jsEngine == null ? message : `${message}, js engine: ${e.jsEngine}`; - const isHandledByLogBox = e.forceRedbox !== true; + const isHandledByLogBox = e.forceRedbox !== true && !global.RN$Bridgeless; const data = preprocessException({ message, diff --git a/Libraries/DeprecatedPropTypes/DeprecatedTextInputPropTypes.js b/Libraries/DeprecatedPropTypes/DeprecatedTextInputPropTypes.js index 275d607b94821d..52a4714d84e914 100644 --- a/Libraries/DeprecatedPropTypes/DeprecatedTextInputPropTypes.js +++ b/Libraries/DeprecatedPropTypes/DeprecatedTextInputPropTypes.js @@ -616,7 +616,6 @@ module.exports = { /** * When `false`, it will prevent the soft keyboard from showing when the field is focused. * Defaults to `true`. - * @platform android */ showSoftInputOnFocus: PropTypes.bool, }; diff --git a/Libraries/FBLazyVector/BUCK b/Libraries/FBLazyVector/BUCK index ea6c50f49ffb9e..d526718e2de05b 100644 --- a/Libraries/FBLazyVector/BUCK +++ b/Libraries/FBLazyVector/BUCK @@ -8,7 +8,7 @@ fb_apple_library( enable_exceptions = False, extension_api_only = True, frameworks = [], - labels = ["supermodule:ios/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], link_whole = False, visibility = ["PUBLIC"], ) diff --git a/Libraries/FBReactNativeSpec/BUCK b/Libraries/FBReactNativeSpec/BUCK index dcef02f8165068..3ab64ac8a7a14a 100644 --- a/Libraries/FBReactNativeSpec/BUCK +++ b/Libraries/FBReactNativeSpec/BUCK @@ -15,7 +15,7 @@ fb_apple_library( "Foundation", "UIKit", ], - labels = ["supermodule:ios/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], reexport_all_header_dependencies = True, deps = [ "//xplat/js/react-native-github:RCTTypeSafety", diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm index 5c242e76747f02..2276aeeed11a71 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm @@ -2301,39 +2301,6 @@ + (RCTManagedPointer *)JS_NativeStatusBarManagerIOS_SpecGetHeightCallbackResult: } // namespace react } // namespace facebook -@implementation RCTCxxConvert (NativeTimePickerAndroid_TimePickerOptions) -+ (RCTManagedPointer *)JS_NativeTimePickerAndroid_TimePickerOptions:(id)json -{ - return facebook::react::managedPointer(json); -} -@end -namespace facebook { - namespace react { - - - static facebook::jsi::Value __hostFunction_NativeTimePickerAndroidSpecJSI_open(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "open", @selector(open:resolve:reject:), args, count); - } - - - NativeTimePickerAndroidSpecJSI::NativeTimePickerAndroidSpecJSI(const ObjCTurboModule::InitParams ¶ms) - : ObjCTurboModule(params) { - - methodMap_["open"] = MethodMetadata {1, __hostFunction_NativeTimePickerAndroidSpecJSI_open}; - - setMethodArgConversionSelector(@"open", 0, @"JS_NativeTimePickerAndroid_TimePickerOptions:"); - - - } - - } // namespace react -} // namespace facebook -@implementation RCTCxxConvert (NativeTimePickerAndroid_TimePickerResult) -+ (RCTManagedPointer *)JS_NativeTimePickerAndroid_TimePickerResult:(id)json -{ - return facebook::react::managedPointer(json); -} -@end namespace facebook { namespace react { @@ -2421,10 +2388,6 @@ + (RCTManagedPointer *)JS_NativeTimePickerAndroid_TimePickerResult:(id)json return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, "getDefaultEventTypes", @selector(getDefaultEventTypes), args, count); } - static facebook::jsi::Value __hostFunction_NativeUIManagerSpecJSI_playTouchSound(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "playTouchSound", @selector(playTouchSound), args, count); - } - static facebook::jsi::Value __hostFunction_NativeUIManagerSpecJSI_lazilyLoadView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, "lazilyLoadView", @selector(lazilyLoadView:), args, count); } @@ -2531,9 +2494,6 @@ + (RCTManagedPointer *)JS_NativeTimePickerAndroid_TimePickerResult:(id)json methodMap_["getDefaultEventTypes"] = MethodMetadata {0, __hostFunction_NativeUIManagerSpecJSI_getDefaultEventTypes}; - methodMap_["playTouchSound"] = MethodMetadata {0, __hostFunction_NativeUIManagerSpecJSI_playTouchSound}; - - methodMap_["lazilyLoadView"] = MethodMetadata {1, __hostFunction_NativeUIManagerSpecJSI_lazilyLoadView}; diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h index ec337621912eeb..29bd6a93843cf8 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h @@ -2370,63 +2370,6 @@ namespace facebook { }; } // namespace react } // namespace facebook - -namespace JS { - namespace NativeTimePickerAndroid { - struct TimePickerOptions { - folly::Optional hour() const; - folly::Optional minute() const; - folly::Optional is24Hour() const; - NSString *mode() const; - - TimePickerOptions(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeTimePickerAndroid_TimePickerOptions) -+ (RCTManagedPointer *)JS_NativeTimePickerAndroid_TimePickerOptions:(id)json; -@end -@protocol NativeTimePickerAndroidSpec - -- (void)open:(JS::NativeTimePickerAndroid::TimePickerOptions &)options - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject; - -@end -namespace facebook { - namespace react { - /** - * ObjC++ class for module 'TimePickerAndroid' - */ - - class JSI_EXPORT NativeTimePickerAndroidSpecJSI : public ObjCTurboModule { - public: - NativeTimePickerAndroidSpecJSI(const ObjCTurboModule::InitParams ¶ms); - - }; - } // namespace react -} // namespace facebook - -namespace JS { - namespace NativeTimePickerAndroid { - struct TimePickerResult { - NSString *action() const; - double hour() const; - double minute() const; - - TimePickerResult(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeTimePickerAndroid_TimePickerResult) -+ (RCTManagedPointer *)JS_NativeTimePickerAndroid_TimePickerResult:(id)json; -@end @protocol NativeTimingSpec - (void)createTimer:(double)callbackID @@ -2542,7 +2485,6 @@ namespace JS { - (NSDictionary *)getConstantsForViewManager:(NSString *)viewManagerName; - (NSArray *)getDefaultEventTypes; -- (void)playTouchSound; - (NSDictionary *)lazilyLoadView:(NSString *)name; - (void)createView:(NSNumber *)reactTag viewName:(NSString *)viewName @@ -3515,41 +3457,6 @@ inline JS::NativeStatusBarManagerIOS::Constants::Builder::Builder(const Input i) inline JS::NativeStatusBarManagerIOS::Constants::Builder::Builder(Constants i) : _factory(^{ return i.unsafeRawValue(); }) {} -inline folly::Optional JS::NativeTimePickerAndroid::TimePickerOptions::hour() const -{ - id const p = _v[@"hour"]; - return RCTBridgingToOptionalDouble(p); -} -inline folly::Optional JS::NativeTimePickerAndroid::TimePickerOptions::minute() const -{ - id const p = _v[@"minute"]; - return RCTBridgingToOptionalDouble(p); -} -inline folly::Optional JS::NativeTimePickerAndroid::TimePickerOptions::is24Hour() const -{ - id const p = _v[@"is24Hour"]; - return RCTBridgingToOptionalBool(p); -} -inline NSString *JS::NativeTimePickerAndroid::TimePickerOptions::mode() const -{ - id const p = _v[@"mode"]; - return RCTBridgingToString(p); -} -inline NSString *JS::NativeTimePickerAndroid::TimePickerResult::action() const -{ - id const p = _v[@"action"]; - return RCTBridgingToString(p); -} -inline double JS::NativeTimePickerAndroid::TimePickerResult::hour() const -{ - id const p = _v[@"hour"]; - return RCTBridgingToDouble(p); -} -inline double JS::NativeTimePickerAndroid::TimePickerResult::minute() const -{ - id const p = _v[@"minute"]; - return RCTBridgingToDouble(p); -} inline JS::NativeToastAndroid::Constants::Builder::Builder(const Input i) : _factory(^{ NSMutableDictionary *d = [NSMutableDictionary new]; auto SHORT = i.SHORT.get(); diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 22b28309076ee1..e5ff46e73827bf 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -15,6 +15,7 @@ const React = require('react'); const ReactNative = require('../Renderer/shims/ReactNative'); // eslint-disable-line no-unused-vars const StyleSheet = require('../StyleSheet/StyleSheet'); +const ImageAnalyticsTagContext = require('./ImageAnalyticsTagContext').default; const flattenStyle = require('../StyleSheet/flattenStyle'); const resolveAssetSource = require('./resolveAssetSource'); @@ -124,14 +125,21 @@ let Image = (props: ImagePropsType, forwardedRef) => { } return ( - + + {analyticTag => { + return ( + + ); + }} + ); }; diff --git a/Libraries/Image/NativeImageEditor.js b/Libraries/Image/NativeImageEditor.js index ff334b897771a2..19dbf3cb6e52a2 100644 --- a/Libraries/Image/NativeImageEditor.js +++ b/Libraries/Image/NativeImageEditor.js @@ -1,5 +1,8 @@ /** - * (c) Facebook, Inc. and its affiliates. Confidential and proprietary. + * 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. * * @flow strict-local * @format @@ -39,7 +42,6 @@ export interface Spec extends TurboModule { +getConstants: () => {||}; +cropImage: ( uri: string, - // eslint-disable-next-line lint/react-native-modules cropData: Options, successCallback: (uri: string) => void, errorCallback: (error: string) => void, diff --git a/Libraries/Image/NativeImageStore.js b/Libraries/Image/NativeImageStore.js index 196b328f7464e6..cbd7b22fd02613 100644 --- a/Libraries/Image/NativeImageStore.js +++ b/Libraries/Image/NativeImageStore.js @@ -1,5 +1,8 @@ /** - * (c) Facebook, Inc. and its affiliates. Confidential and proprietary. + * 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. * * @flow strict-local * @format diff --git a/Libraries/Image/RCTImageLoader.mm b/Libraries/Image/RCTImageLoader.mm index 88ed783c52c1e7..90c7a3b5ef7287 100644 --- a/Libraries/Image/RCTImageLoader.mm +++ b/Libraries/Image/RCTImageLoader.mm @@ -635,7 +635,7 @@ - (RCTImageURLLoaderRequest *)_loadImageOrDataWithURLRequest:(NSURLRequest *)req } } }); - + return [[RCTImageURLLoaderRequest alloc] initWithRequestId:requestId imageURL:request.URL cancellationBlock:^{ BOOL alreadyCancelled = atomic_fetch_or(cancelled.get(), 1); if (alreadyCancelled) { @@ -875,12 +875,24 @@ - (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequ } } +- (void)trackURLImageRequestDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest +{ + if (!loaderRequest) { + return; + } + + id loadHandler = [self imageURLLoaderForURL:loaderRequest.imageURL]; + if ([loadHandler respondsToSelector:@selector(trackURLImageRequestDidDestroy:)]) { + [(id)loadHandler trackURLImageRequestDidDestroy:loaderRequest]; + } +} + - (void)trackURLImageDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest { if (!loaderRequest) { return; } - + id loadHandler = [self imageURLLoaderForURL:loaderRequest.imageURL]; if ([loadHandler respondsToSelector:@selector(trackURLImageDidDestroy:)]) { [(id)loadHandler trackURLImageDidDestroy:loaderRequest]; diff --git a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h index 664f800179857b..5f02703ecf5b4d 100644 --- a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h +++ b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h @@ -45,6 +45,11 @@ RCT_EXTERN void RCTEnableImageLoadingPerfInstrumentation(BOOL enabled); */ - (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequest imageView:(RCTUIView *)imageView; // TODO(macOS GH#774) +/** + * Image instrumentation - notify that the request was cancelled. + */ +- (void)trackURLImageRequestDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest; + /** * Image instrumentation - notify that the native image view was destroyed. */ diff --git a/Libraries/Image/RCTImageURLLoaderWithAttribution.h b/Libraries/Image/RCTImageURLLoaderWithAttribution.h index 43b4dcd4f30ac4..69587aaf07a5f7 100644 --- a/Libraries/Image/RCTImageURLLoaderWithAttribution.h +++ b/Libraries/Image/RCTImageURLLoaderWithAttribution.h @@ -18,6 +18,7 @@ namespace react { struct ImageURLLoaderAttribution { int32_t nativeViewTag = 0; int32_t surfaceId = 0; + NSString *analyticTag; }; } // namespace react @@ -69,6 +70,11 @@ struct ImageURLLoaderAttribution { */ - (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequest imageView:(RCTUIView *)imageView; // TODO(macOS GH#774) +/** + * Image instrumentation - notify that the request was destroyed. + */ +- (void)trackURLImageRequestDidDestroy:(RCTImageURLLoaderRequest *)loaderRequest; + /** * Image instrumentation - notify that the native image view was destroyed. */ diff --git a/Libraries/Image/RCTImageView.h b/Libraries/Image/RCTImageView.h index 5e3064f166be27..90c7e79e26f0ae 100644 --- a/Libraries/Image/RCTImageView.h +++ b/Libraries/Image/RCTImageView.h @@ -30,6 +30,7 @@ typedef NS_ENUM(NSInteger, UIImageRenderingMode) { @property (nonatomic, copy) NSArray *imageSources; @property (nonatomic, assign) CGFloat blurRadius; @property (nonatomic, assign) RCTResizeMode resizeMode; +@property (nonatomic, copy) NSString *internal_analyticTag; #if TARGET_OS_OSX // [TODO(macOS GH#774) @property (nonatomic, copy) NSColor *tintColor; diff --git a/Libraries/Image/RCTImageView.mm b/Libraries/Image/RCTImageView.mm index e41c45cc10cbb4..8a14c4c137ae95 100644 --- a/Libraries/Image/RCTImageView.mm +++ b/Libraries/Image/RCTImageView.mm @@ -317,28 +317,34 @@ - (void)setResizeMode:(RCTResizeMode)resizeMode } } +- (void)setInternal_analyticTag:(NSString *)internal_analyticTag { + if (_internal_analyticTag != internal_analyticTag) { + _internal_analyticTag = internal_analyticTag; + _needsReload = YES; + } +} + - (void)cancelImageLoad { - if (_loaderRequest.cancellationBlock) { - _loaderRequest.cancellationBlock(); - } - - _loaderRequest = nil; + [_loaderRequest cancel]; _pendingImageSource = nil; } -- (void)clearImage +- (void)cancelAndClearImageLoad { [self cancelImageLoad]; - self.image = nil; - _imageSource = nil; + + [_imageLoader trackURLImageRequestDidDestroy:_loaderRequest]; + _loaderRequest = nil; } #if !TARGET_OS_OSX // TODO(macOS GH#774) - (void)clearImageIfDetached { if (!self.window) { - [self clearImage]; + [self cancelAndClearImageLoad]; + self.image = nil; + _imageSource = nil; } } #endif // TODO(macOS GH#774) @@ -399,7 +405,7 @@ - (BOOL)shouldChangeImageSource - (void)reloadImage { - [self cancelImageLoad]; + [self cancelAndClearImageLoad]; _needsReload = NO; RCTImageSource *source = [self imageSourceForSize:self.frame.size]; @@ -454,13 +460,14 @@ - (void)reloadImage attribution:{ .nativeViewTag = [self.reactTag intValue], .surfaceId = [self.rootTag intValue], + .analyticTag = self.internal_analyticTag } progressBlock:progressHandler partialLoadBlock:partialLoadHandler completionBlock:completionHandler]; _loaderRequest = loaderRequest; } else { - [self clearImage]; + [self cancelAndClearImageLoad]; } } @@ -623,6 +630,7 @@ - (void)didMoveToWindow // prioritise image requests that are actually on-screen, this removes // requests that have gotten "stuck" from the queue, unblocking other images // from loading. + // Do not clear _loaderRequest because this component can be visible again without changing image source [self cancelImageLoad]; } else if ([self shouldChangeImageSource]) { [self reloadImage]; diff --git a/Libraries/Image/RCTImageViewManager.mm b/Libraries/Image/RCTImageViewManager.mm index 67d3a29d167194..92e24a6fa752ed 100644 --- a/Libraries/Image/RCTImageViewManager.mm +++ b/Libraries/Image/RCTImageViewManager.mm @@ -40,6 +40,7 @@ - (RCTPlatformView *)view // TODO(macOS GH#774) RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(resizeMode, RCTResizeMode) +RCT_EXPORT_VIEW_PROPERTY(internal_analyticTag, NSString) RCT_REMAP_VIEW_PROPERTY(source, imageSources, NSArray); RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView) { diff --git a/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap b/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap index 1bcd31396ff7d4..ddb928125e2145 100644 --- a/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap +++ b/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap @@ -12,6 +12,7 @@ exports[` should render as when mocked 1`] = ` exports[` should render as when not mocked 1`] = ` 0) { + event.persist(); this._hoverInDelayTimeout = setTimeout(() => { onHoverIn(event); }, delayHoverIn); @@ -627,6 +628,7 @@ export default class Pressability { this._config.delayHoverOut, ); if (delayHoverOut > 0) { + event.persist(); this._hoverInDelayTimeout = setTimeout(() => { onHoverOut(event); }, delayHoverOut); @@ -764,6 +766,7 @@ export default class Pressability { normalizeDelay(this._config.delayPressOut), ); if (delayPressOut > 0) { + event.persist(); this._pressOutDelayTimeout = setTimeout(() => { onPressOut(event); }, delayPressOut); @@ -787,16 +790,7 @@ export default class Pressability { } _measureCallback = (left, top, width, height, pageX, pageY) => { - if ( - !( - left > 0 || - top > 0 || - width > 0 || - height > 0 || - pageX > 0 || - pageY > 0 - ) - ) { + if (!left && !top && !width && !height && !pageX && !pageY) { return; } this._responderRegion = { diff --git a/Libraries/RCTRequired/BUCK b/Libraries/RCTRequired/BUCK index 396f41b3f98e50..18d34dc7750d91 100644 --- a/Libraries/RCTRequired/BUCK +++ b/Libraries/RCTRequired/BUCK @@ -7,5 +7,5 @@ fb_apple_library( contacts = ["oncall+react_native@xmail.facebook.com"], extension_api_only = True, frameworks = ["Foundation"], - labels = ["supermodule:ios/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], ) diff --git a/Libraries/ReactNative/DummyUIManager.js b/Libraries/ReactNative/DummyUIManager.js index bf918219c79496..ba816c1cb3c444 100644 --- a/Libraries/ReactNative/DummyUIManager.js +++ b/Libraries/ReactNative/DummyUIManager.js @@ -22,7 +22,6 @@ module.exports = { getConstants: (): {...} => ({}), getConstantsForViewManager: (viewManagerName: string) => {}, getDefaultEventTypes: (): Array<$FlowFixMe> => [], - playTouchSound: () => {}, lazilyLoadView: (name: string) => {}, createView: ( reactTag: ?number, diff --git a/Libraries/ReactNative/NativeUIManager.js b/Libraries/ReactNative/NativeUIManager.js index 717e63dbc12282..30e2d1f2c7d0df 100644 --- a/Libraries/ReactNative/NativeUIManager.js +++ b/Libraries/ReactNative/NativeUIManager.js @@ -17,7 +17,6 @@ export interface Spec extends TurboModule { +getConstants: () => Object; +getConstantsForViewManager: (viewManagerName: string) => Object; +getDefaultEventTypes: () => Array; - +playTouchSound: () => void; +lazilyLoadView: (name: string) => Object; // revisit return +createView: ( reactTag: ?number, diff --git a/Libraries/ReactNative/UIManagerProperties.js b/Libraries/ReactNative/UIManagerProperties.js index 3205d7ea252e2e..938e42eef9d4ad 100644 --- a/Libraries/ReactNative/UIManagerProperties.js +++ b/Libraries/ReactNative/UIManagerProperties.js @@ -40,7 +40,6 @@ module.exports = [ 'measureInWindow', 'measureLayout', 'measureLayoutRelativeToParent', - 'playTouchSound', 'removeRootView', 'removeSubviewsFromContainerWithID', 'replaceExistingNonRootView', diff --git a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h index 61d539f1e61717..b593c4c863476b 100644 --- a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) UIEdgeInsets textContainerInset; #if !TARGET_OS_OSX // TODO(macOS GH#774) @property (nonatomic, strong, nullable) UIView *inputAccessoryView; +@property (nonatomic, strong, nullable) UIView *inputView; #endif // TODO(macOS GH#774) @property (nonatomic, weak, nullable) id textInputDelegate; @property (nonatomic, readonly) CGSize contentSize; diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.h b/Libraries/Text/TextInput/RCTBaseTextInputView.h index 75cde85e7a71d4..992d34b07c00c5 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.h +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.h @@ -57,6 +57,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSString *inputAccessoryViewID; #if !TARGET_OS_OSX // TODO(macOS GH#774) @property (nonatomic, assign) UIKeyboardType keyboardType; +@property (nonatomic, assign) BOOL showSoftInputOnFocus; #endif // TODO(macOS GH#774) /** diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m index ccabbb2e922da0..c1e4222f765173 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -348,6 +348,18 @@ - (void)setKeyboardType:(UIKeyboardType)keyboardType } } } + +- (void)setShowSoftInputOnFocus:(BOOL)showSoftInputOnFocus +{ + (void)_showSoftInputOnFocus; + if (showSoftInputOnFocus) { + // Resets to default keyboard. + self.backedTextInputView.inputView = nil; + } else { + // Hides keyboard, but keeps blinking cursor. + self.backedTextInputView.inputView = [[UIView alloc] init]; + } +} #endif // TODO(macOS GH#774) #pragma mark - RCTBackedTextInputDelegate diff --git a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m index 4f764b2c7bceb4..81def532f60c6a 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputViewManager.m @@ -58,6 +58,7 @@ @implementation RCTBaseTextInputViewManager RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL) RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(clearTextOnFocus, BOOL) // TODO(macOS GH#774) RCT_REMAP_NOT_OSX_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) // TODO(macOS GH#774) +RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(showSoftInputOnFocus, BOOL) // TODO(macOS GH#774) RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber) RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(selectTextOnFocus, BOOL) // TODO(macOS GH#774) RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection) diff --git a/Libraries/TimePickerAndroid/NativeTimePickerAndroid.js b/Libraries/TimePickerAndroid/NativeTimePickerAndroid.js deleted file mode 100644 index 46594ffdc3196e..00000000000000 --- a/Libraries/TimePickerAndroid/NativeTimePickerAndroid.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * (c) Facebook, Inc. and its affiliates. Confidential and proprietary. - * - * @flow strict-local - * @format - */ - -'use strict'; - -import type {TurboModule} from '../TurboModule/RCTExport'; -import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; - -export type TimePickerOptions = {| - hour?: number, - minute?: number, - is24Hour?: boolean, - mode?: string, -|}; - -export type TimePickerResult = {| - action: string, - hour: number, - minute: number, -|}; - -export interface Spec extends TurboModule { - // eslint-disable-next-line lint/react-native-modules - +open: (options: TimePickerOptions) => Promise; -} - -export default (TurboModuleRegistry.get('TimePickerAndroid'): ?Spec); diff --git a/Libraries/Utilities/HMRClient.js b/Libraries/Utilities/HMRClient.js index ca6d8ef5a4b14e..e653ab89ff0ba5 100644 --- a/Libraries/Utilities/HMRClient.js +++ b/Libraries/Utilities/HMRClient.js @@ -16,6 +16,7 @@ const MetroHMRClient = require('metro/src/lib/bundle-modules/HMRClient'); const Platform = require('./Platform'); const prettyFormat = require('pretty-format'); +import getDevServer from '../Core/Devtools/getDevServer'; import NativeRedBox from '../NativeModules/specs/NativeRedBox'; import * as LogBoxData from '../LogBox/Data/LogBoxData'; import type {ExtendedError} from '../Core/Devtools/parseErrorStack'; @@ -159,8 +160,15 @@ const HMRClient: HMRClientNativeInterface = { const client = new MetroHMRClient(`ws://${wsHost}/hot`); hmrClient = client; + const {fullBundleUrl} = getDevServer(); pendingEntryPoints.push( - `ws://${wsHost}/hot?bundleEntry=${bundleEntry}&platform=${platform}`, + // HMRServer understands regular bundle URLs, so prefer that in case + // there are any important URL parameters we can't reconstruct from + // `setup()`'s arguments. + fullBundleUrl ?? + // The ws://.../hot?bundleEntry= format is an alternative to specifying + // a regular HTTP bundle URL. + `ws://${wsHost}/hot?bundleEntry=${bundleEntry}&platform=${platform}`, ); client.on('connection-error', e => { diff --git a/Libraries/Utilities/LoadingView.ios.js b/Libraries/Utilities/LoadingView.ios.js index ad2caad48310ea..bc423a822ae7c0 100644 --- a/Libraries/Utilities/LoadingView.ios.js +++ b/Libraries/Utilities/LoadingView.ios.js @@ -12,25 +12,38 @@ import processColor from '../StyleSheet/processColor'; import NativeDevLoadingView from './NativeDevLoadingView'; +import Appearance from './Appearance'; module.exports = { showMessage(message: string, type: 'load' | 'refresh') { if (NativeDevLoadingView) { - const loadColor = processColor('#404040'); - const refreshColor = processColor('#2584e8'); - const white = processColor('#ffffff'); + if (type === 'refresh') { + const backgroundColor = processColor('#2584e8'); + const textColor = processColor('#ffffff'); - NativeDevLoadingView.showMessage( - message, - typeof white === 'number' ? white : null, - type && type === 'load' - ? typeof loadColor === 'number' - ? loadColor - : null - : typeof refreshColor === 'number' - ? refreshColor - : null, - ); + NativeDevLoadingView.showMessage( + message, + typeof textColor === 'number' ? textColor : null, + typeof backgroundColor === 'number' ? backgroundColor : null, + ); + } else if (type === 'load') { + let backgroundColor; + let textColor; + + if (Appearance.getColorScheme() === 'dark') { + backgroundColor = processColor('#fafafa'); + textColor = processColor('#242526'); + } else { + backgroundColor = processColor('#404040'); + textColor = processColor('#ffffff'); + } + + NativeDevLoadingView.showMessage( + message, + typeof textColor === 'number' ? textColor : null, + typeof backgroundColor === 'number' ? backgroundColor : null, + ); + } } }, hide() { diff --git a/Libraries/Utilities/MatrixMath.js b/Libraries/Utilities/MatrixMath.js index 2a943a8961ac03..f316bea68611fc 100755 --- a/Libraries/Utilities/MatrixMath.js +++ b/Libraries/Utilities/MatrixMath.js @@ -651,10 +651,6 @@ const MatrixMath = { skew[0] = MatrixMath.v3Dot(row[0], row[1]); row[1] = MatrixMath.v3Combine(row[1], row[0], 1.0, -skew[0]); - // Compute XY shear factor and make 2nd row orthogonal to 1st. - skew[0] = MatrixMath.v3Dot(row[0], row[1]); - row[1] = MatrixMath.v3Combine(row[1], row[0], 1.0, -skew[0]); - // Now, compute Y scale and normalize 2nd row. scale[1] = MatrixMath.v3Length(row[1]); row[1] = MatrixMath.v3Normalize(row[1], scale[1]); diff --git a/Libraries/Utilities/ReactNativeTestTools.js b/Libraries/Utilities/ReactNativeTestTools.js index cd018a6fc5e3cc..f6dadab9b7da93 100644 --- a/Libraries/Utilities/ReactNativeTestTools.js +++ b/Libraries/Utilities/ReactNativeTestTools.js @@ -16,8 +16,8 @@ const React = require('react'); const ReactTestRenderer = require('react-test-renderer'); const ShallowRenderer = require('react-test-renderer/shallow'); -/* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses an error - * found when Flow v0.122.0 was deployed. To see the error, delete this comment +/* $FlowFixMe(>=0.125.1 site=react_native_fb) This comment suppresses an error + * found when Flow v0.125.1 was deployed. To see the error, delete this comment * and run Flow. */ const shallowRenderer = new ShallowRenderer(); @@ -28,8 +28,8 @@ export type ReactTestInstance = $PropertyType; export type Predicate = (node: ReactTestInstance) => boolean; type $ReturnType = $Call<((...A) => Ret) => Ret, Fn>; -/* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses an error - * found when Flow v0.122.0 was deployed. To see the error, delete this comment +/* $FlowFixMe(>=0.125.1 site=react_native_fb) This comment suppresses an error + * found when Flow v0.125.1 was deployed. To see the error, delete this comment * and run Flow. */ export type ReactTestRendererJSON = $ReturnType; @@ -56,12 +56,12 @@ function byClickable(): Predicate { // HACK: Find components that use `Pressability`. node.instance?.state?.pressability != null || // TODO: Remove this after deleting `Touchable`. - /* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses an - * error found when Flow v0.122.0 was deployed. To see the error, delete + /* $FlowFixMe(>=0.125.1 site=react_native_fb) This comment suppresses an + * error found when Flow v0.125.1 was deployed. To see the error, delete * this comment and run Flow. */ (node.instance && - /* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses - * an error found when Flow v0.122.0 was deployed. To see the error, + /* $FlowFixMe(>=0.125.1 site=react_native_fb) This comment suppresses + * an error found when Flow v0.125.1 was deployed. To see the error, * delete this comment and run Flow. */ typeof node.instance.touchableHandlePress === 'function'), 'is clickable', @@ -77,8 +77,8 @@ function byTestID(testID: string): Predicate { function byTextMatching(regex: RegExp): Predicate { return withMessage( - /* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses an - * error found when Flow v0.122.0 was deployed. To see the error, delete + /* $FlowFixMe(>=0.125.1 site=react_native_fb) This comment suppresses an + * error found when Flow v0.125.1 was deployed. To see the error, delete * this comment and run Flow. */ node => node.props && regex.exec(node.props.children), `text content matches ${regex.toString()}`, diff --git a/RNTester/Podfile.lock b/RNTester/Podfile.lock index a5767ec8323f91..81fbcd80bd1d8e 100644 --- a/RNTester/Podfile.lock +++ b/RNTester/Podfile.lock @@ -99,6 +99,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/ARTHeaders (1000.0.0): - glog @@ -107,6 +108,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/CoreModulesHeaders (1000.0.0): - glog @@ -115,6 +117,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/Default (1000.0.0): - glog @@ -122,6 +125,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/DevSupport (1000.0.0): - glog @@ -132,6 +136,7 @@ PODS: - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) - React-jsinspector (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTActionSheetHeaders (1000.0.0): - glog @@ -140,6 +145,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTAnimationHeaders (1000.0.0): - glog @@ -148,6 +154,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTBlobHeaders (1000.0.0): - glog @@ -156,6 +163,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTImageHeaders (1000.0.0): - glog @@ -164,6 +172,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTLinkingHeaders (1000.0.0): - glog @@ -172,6 +181,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTNetworkHeaders (1000.0.0): - glog @@ -180,6 +190,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTPushNotificationHeaders (1000.0.0): - glog @@ -188,6 +199,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTSettingsHeaders (1000.0.0): - glog @@ -196,6 +208,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTTextHeaders (1000.0.0): - glog @@ -204,6 +217,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTVibrationHeaders (1000.0.0): - glog @@ -212,6 +226,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-Core/RCTWebSocket (1000.0.0): - glog @@ -220,6 +235,7 @@ PODS: - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) - React-jsiexecutor (= 1000.0.0) + - React-perflogger (= 1000.0.0) - Yoga - React-CoreModules (1000.0.0): - FBReactNativeSpec (= 1000.0.0) @@ -236,6 +252,7 @@ PODS: - RCT-Folly (= 2020.01.13.00) - React-callinvoker (= 1000.0.0) - React-jsinspector (= 1000.0.0) + - React-perflogger (= 1000.0.0) - React-runtimeexecutor (= 1000.0.0) - React-jsi (1000.0.0): - boost-for-react-native (= 1.63.0) @@ -254,7 +271,9 @@ PODS: - RCT-Folly (= 2020.01.13.00) - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) + - React-perflogger (= 1000.0.0) - React-jsinspector (1000.0.0) + - React-perflogger (1000.0.0) - React-RCTActionSheet (1000.0.0): - React-Core/RCTActionSheetHeaders (= 1000.0.0) - React-RCTAnimation (1000.0.0): @@ -342,6 +361,7 @@ PODS: - React-Core (= 1000.0.0) - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) + - React-perflogger (= 1000.0.0) - ReactCommon/turbomodule/samples (1000.0.0): - DoubleConversion - glog @@ -350,6 +370,7 @@ PODS: - React-Core (= 1000.0.0) - React-cxxreact (= 1000.0.0) - React-jsi (= 1000.0.0) + - React-perflogger (= 1000.0.0) - ReactCommon/turbomodule/core (= 1000.0.0) - Yoga (1.14.0) - YogaKit (1.18.1): @@ -394,6 +415,7 @@ DEPENDENCIES: - React-jsi (from `../ReactCommon/jsi`) - React-jsiexecutor (from `../ReactCommon/jsiexecutor`) - React-jsinspector (from `../ReactCommon/jsinspector`) + - React-perflogger (from `../ReactCommon/reactperflogger`) - React-RCTActionSheet (from `../Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../Libraries/NativeAnimation`) - React-RCTBlob (from `../Libraries/Blob`) @@ -461,6 +483,8 @@ EXTERNAL SOURCES: :path: "../ReactCommon/jsiexecutor" React-jsinspector: :path: "../ReactCommon/jsinspector" + React-perflogger: + :path: "../ReactCommon/reactperflogger" React-RCTActionSheet: :path: "../Libraries/ActionSheetIOS" React-RCTAnimation: @@ -499,8 +523,8 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f DoubleConversion: 2b45d0f8e156a5b02354c8a4062de64d41ccb4e0 - FBLazyVector: 98b5ed9e2c8215c32630adabb6c6ae8fd88b4808 - FBReactNativeSpec: a5a59afb795ccab0596667e248b5cdcb05cf86e2 + FBLazyVector: dac58e415545ed3aaa631054fe0447782a933865 + FBReactNativeSpec: d25a00a4a4d0392ed51a01fcdc33b3445bae2957 Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 @@ -511,33 +535,34 @@ SPEC CHECKSUMS: glog: 789873d01e4b200777d0a09bc23d548446758699 OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 RCT-Folly: 55d0039b24e192081ec0b2257f7bd9f42e382fb7 - RCTRequired: cd124a69447b4db403af1f4dc1d7c7cb617185a0 - RCTTypeSafety: 83da012f103c272792343e635da1b72936067b96 - React: e7aaeceb4fee9ffc841301330e753a1bd3e57437 - React-ART: 114994073be3e1257406f49d629222d94f5361bd - React-callinvoker: ebde245fca99f1f324793be8783dd6def592627c - React-Core: 4775366e8e941f063ee8463f86bf41613ec1a7b2 - React-CoreModules: ca9df8e2a54ccbaad588ffa772f71fbc8ba19279 - React-cxxreact: cfba7c9e9bd2d23b97af6467d5ecf390e932676d - React-jsi: b0857c300ffbf312d1b15e60a060ad7f5a2f978d - React-jsiexecutor: 93e0bb19579e3002775bc3efc0cbd81f20943be8 - React-jsinspector: 3526475c50fc3d942d6cdc02618f72a5f5510935 - React-RCTActionSheet: 23ed110450142634ede6b88ccbd2fb69aa892c52 - React-RCTAnimation: 383e388d729b39d0087b4986f0ebb728bac7eb02 - React-RCTBlob: 53fcb292e073976d7f6867841eee402770bfbf84 - React-RCTImage: 3ea30c5c2fd33f042aa768e6a1688046bbf59bda - React-RCTLinking: cb77eeacb78c1b3cd1c579c6597ea2bbe424945e - React-RCTNetwork: a34eabaa807c9163af30d4e4623b20e3136659bc - React-RCTPushNotification: 9f0a55cbaeccb9a10252d468fa73c7281aef680c - React-RCTSettings: 149f8ea51af3afb60f4a3a262698b94a0a9209aa - React-RCTTest: 7d56585d7cc88320631522ae29ffba082e12c377 - React-RCTText: 9b42805a9082c6e6df5b0deab2bdba14f1db0e3a - React-RCTVibration: c834fb5115f6d341b68a9595905bf253b617ab1d - React-runtimeexecutor: 576ce90cf13a65e00b35c3cbcdbe65aaddea8351 + RCTRequired: 212ed857d2e02aa7a950e2e9fd1e0e14bf36dac9 + RCTTypeSafety: cc7500b093bff4f94b61c64033c26f9c9b613ed6 + React: 1fbebfa33be059ef6f440ac79e568d7cc2f881db + React-ART: fda22d56018ca90ce94d0775f756acc88f700041 + React-callinvoker: 6b4291b2f7baa371074a21bc01c1ae3469159f26 + React-Core: 7587308afd5f755599465e6c8e0d111fe7294370 + React-CoreModules: 2845f7f2e5487b7146b8fcfb28603e7aba8c73dd + React-cxxreact: b79fc1d424cdedac912585efd2fb8481c963185b + React-jsi: 4ff577747833dbf79aadf814d0a7103b70566ba7 + React-jsiexecutor: 64210b838560d2aaf1ed69f514894961cf43f684 + React-jsinspector: 3fd9965e147de74ce261fdb58fef297b2973b84e + React-perflogger: 097bc54ba46a5733759303edb807834888c694de + React-RCTActionSheet: d0520c2146f744d28915281af40b8421c549a648 + React-RCTAnimation: 87c74e6bdb902190345b90df4878a018dd62d3f3 + React-RCTBlob: 1ed3cccb9eba04212a0779493902b4673605f79f + React-RCTImage: 1df1bc4d3f172c43c053d881befd4e189b58930e + React-RCTLinking: 35ce87cc530d4c9945d7c4f457a3d370f0c22361 + React-RCTNetwork: 450019d7b09925c10210be67268870761abc560a + React-RCTPushNotification: 3638092f4cc05336049fe360d45dfc64e1f662b4 + React-RCTSettings: 90fbb7c75497e6a68a60fd3d155cec3aa9ec3905 + React-RCTTest: 703fefa06f06aeeb412b2b4912c7104517bdf0a7 + React-RCTText: 34cd0c240c00bfe8b942c5a3038169754a902200 + React-RCTVibration: 2d2b494b2d8795b8382545bf31a686cc39056bc7 + React-runtimeexecutor: 56de50ce596344a7d1fbc697149b844183e9ff9b React-TurboModuleCxx-RNW: 18bb71af41fe34c8b12a56bef60aae7ee32b0817 - React-TurboModuleCxx-WinRTPort: d4886bfcdff8fb04d407e6a3b7ee8feb7cc2ad40 - ReactCommon: 4ae7a0aa58b93673a204fdd12ff4f8b079baf20e - Yoga: dd900352619ba7f891e6e505ceee59afc0012115 + React-TurboModuleCxx-WinRTPort: 892e11a61325fcab19632767b4e4d0fb6979ae33 + ReactCommon: 574163ae6b6797f54c228ad305bc7a52afa1cef9 + Yoga: ba8ee169a50208f5ab580225e339f8d7f6a46e22 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a PODFILE CHECKSUM: 7d43a928a9b9ad27329da110adbfadd923a39ba8 diff --git a/RNTester/README.md b/RNTester/README.md index 4efa9b297ab165..e286de8cb51702 100644 --- a/RNTester/README.md +++ b/RNTester/README.md @@ -14,8 +14,8 @@ Before running the app, make sure you ran: Both macOS and Xcode are required. -- Install CocoaPods. We installing CocoaPods using [Homebrew](http://brew.sh/): `brew install cocoapods` -- Run `cd RNTester; pod install` +- Install [Bundler](https://bundler.io/): `gem install bundler`. We use bundler to install the right version of [CocoaPods](https://cocoapods.org/) locally. +- Install Bundler and CocoaPods dependencies: `bundle install && bundle exec pod install` - Open the generated `RNTesterPods.xcworkspace`. This is not checked in, as it is generated by CocoaPods. Do not open `RNTesterPods.xcodeproj` directly. ### Running on Android diff --git a/RNTester/RNTester-macOS/AppDelegate.mm b/RNTester/RNTester-macOS/AppDelegate.mm index 6a938847502593..7951d625e53af2 100644 --- a/RNTester/RNTester-macOS/AppDelegate.mm +++ b/RNTester/RNTester-macOS/AppDelegate.mm @@ -98,6 +98,15 @@ - (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker]; + +#if RCT_DEV + /** + * Eagerly initialize RCTDevMenu so CMD + d, CMD + i, and CMD + r work. + * This is a stop gap until we have a system to eagerly init Turbo Modules. + */ + [_turboModuleManager moduleForName:"RCTDevMenu"]; +#endif + __weak __typeof(self) weakSelf = self; return std::make_unique( facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) { @@ -132,11 +141,7 @@ - (Class)getModuleClassFromName:(const char *)name return _turboModulesProvider->getModule(name, jsInvoker); } -- (std::shared_ptr)getTurboModule:(const std::string &)name - instance:(id)instance - jsInvoker:(std::shared_ptr)jsInvoker - nativeInvoker:(std::shared_ptr)nativeInvoker - perfLogger:(id)perfLogger +- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { return nullptr; } diff --git a/RNTester/RNTester/AppDelegate.mm b/RNTester/RNTester/AppDelegate.mm index 7e6e7fd10182e0..df27fa1addf244 100644 --- a/RNTester/RNTester/AppDelegate.mm +++ b/RNTester/RNTester/AppDelegate.mm @@ -161,6 +161,15 @@ - (void)loadSourceForBridge:(RCTBridge *)bridge _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker]; + +#if RCT_DEV + /** + * Eagerly initialize RCTDevMenu so CMD + d, CMD + i, and CMD + r work. + * This is a stop gap until we have a system to eagerly init Turbo Modules. + */ + [_turboModuleManager moduleForName:"RCTDevMenu"]; +#endif + __weak __typeof(self) weakSelf = self; return std::make_unique( facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) { diff --git a/RNTester/RNTesterPods.xcodeproj/project.pbxproj b/RNTester/RNTesterPods.xcodeproj/project.pbxproj index b7a8859d7683d5..563893e2e592da 100644 --- a/RNTester/RNTesterPods.xcodeproj/project.pbxproj +++ b/RNTester/RNTesterPods.xcodeproj/project.pbxproj @@ -1558,6 +1558,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 5BEC8567F3741044B6A5EFC5 /* Pods-RNTester.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CODE_SIGN_ENTITLEMENTS = RNTester/RNTester.entitlements; diff --git a/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m b/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m index 6d2a2b24ad1b8c..386565f6d2ee40 100644 --- a/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m +++ b/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m @@ -20,12 +20,12 @@ static NSURL *localhostBundleURL() { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=%@&dev=true&minify=false", testFile, kRCTPlatformName]]; // TODO(macOS ISS#3536887) + return [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=%@&dev=true&minify=false&app=com.apple.dt.xctest.tool", testFile, kRCTPlatformName]]; // TODO(macOS ISS#3536887) } static NSURL *ipBundleURL() { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.1.1:8081/%@.bundle?platform=%@&dev=true&minify=false", testFile, kRCTPlatformName]]; // TODO(macOS ISS#3536887) + return [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.1.1:8081/%@.bundle?platform=%@&dev=true&minify=false&app=com.apple.dt.xctest.tool", testFile, kRCTPlatformName]]; // TODO(macOS ISS#3536887) } @implementation NSBundle (RCTBundleURLProviderTests) diff --git a/RNTester/js/components/TextInlineView.js b/RNTester/js/components/TextInlineView.js index e5fcc68aaeac4c..a15762131b5d22 100644 --- a/RNTester/js/components/TextInlineView.js +++ b/RNTester/js/components/TextInlineView.js @@ -25,6 +25,21 @@ function Basic(): React.Node { ); } +function NestedTexts(): React.Node { + return ( + + This is the first row + + + This is a nested text + + with a Red View + + + + ); +} + function ClippedByText(): React.Node { return ( @@ -200,6 +215,7 @@ class ChangeInnerViewSize extends React.Component<*, ChangeSizeState> { module.exports = { Basic, + NestedTexts, ClippedByText, ChangeImageSize, ChangeViewSize, diff --git a/RNTester/js/examples/ScrollView/ScrollViewExample.js b/RNTester/js/examples/ScrollView/ScrollViewExample.js index c00ea24ea83ae3..b54a6811adfec4 100644 --- a/RNTester/js/examples/ScrollView/ScrollViewExample.js +++ b/RNTester/js/examples/ScrollView/ScrollViewExample.js @@ -287,6 +287,38 @@ if (Platform.OS === 'ios') { return ; }, }); + exports.examples.push({ + title: ' (centerContent = true)\n', + description: + 'ScrollView puts its content in the center if the content is smaller than scroll view', + render: function(): React.Node { + function CenterContentList(): React.Node { + return ( + + This should be in center. + + ); + } + return ; + }, + }); + exports.examples.push({ + title: ' (contentOffset = {x: 100, y: 0})\n', + description: 'Initial contentOffset can be set on ScrollView.', + render: function(): React.Node { + function CenterContentList(): React.Node { + return ( + + {ITEMS.map(createItemRow)} + + ); + } + return ; + }, + }); } class Item extends React.PureComponent<{| diff --git a/RNTester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js b/RNTester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js new file mode 100644 index 00000000000000..8b06f064fda39f --- /dev/null +++ b/RNTester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js @@ -0,0 +1,114 @@ +/** + * 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 + * @flow strict-local + */ + +'use strict'; + +import * as React from 'react'; +import {SegmentedControlIOS, Text, View, StyleSheet} from 'react-native'; + +export function BasicSegmentedControlExample(): React.Node { + return ( + + + + + + + + + ); +} + +export function PreSelectedSegmentedControlExample(): React.Node { + return ( + + + + + + ); +} + +export function MomentarySegmentedControlExample(): React.Node { + return ( + + + + + + ); +} + +export function DisabledSegmentedControlExample(): React.Node { + return ( + + + + + + ); +} + +export function ColorSegmentedControlExample(): React.Node { + return ( + + + + + + + + + ); +} + +export function EventSegmentedControlExample(): React.Node { + const [selectedIndex, setSelectedIndex] = React.useState(undefined); + const [value, setValue] = React.useState('Not selected'); + const values = ['One', 'Two', 'Three']; + + return ( + + Value: {value} + Index: {selectedIndex} + { + setSelectedIndex(event.nativeEvent.selectedSegmentIndex); + }} + onValueChange={changedValue => { + setValue(changedValue); + }} + /> + + ); +} + +const styles = StyleSheet.create({ + text: { + fontSize: 14, + textAlign: 'center', + fontWeight: '500', + margin: 10, + }, +}); diff --git a/RNTester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js b/RNTester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js index 0c20da7b148ef6..2824475b838f6a 100644 --- a/RNTester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js +++ b/RNTester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js @@ -11,134 +11,14 @@ 'use strict'; const React = require('react'); -const {SegmentedControlIOS, Text, View, StyleSheet} = require('react-native'); - -class BasicSegmentedControlExample extends React.Component<{...}> { - render() { - return ( - - - - - - - - - ); - } -} - -class PreSelectedSegmentedControlExample extends React.Component<{...}> { - render() { - return ( - - - - - - ); - } -} - -class MomentarySegmentedControlExample extends React.Component<{...}> { - render() { - return ( - - - - - - ); - } -} - -class DisabledSegmentedControlExample extends React.Component<{...}> { - render() { - return ( - - - - - - ); - } -} - -class ColorSegmentedControlExample extends React.Component<{...}> { - render() { - return ( - - - - - - - - - ); - } -} - -class EventSegmentedControlExample extends React.Component< - {...}, - $FlowFixMeState, -> { - state = { - values: ['One', 'Two', 'Three'], - value: 'Not selected', - selectedIndex: undefined, - }; - - render() { - return ( - - Value: {this.state.value} - Index: {this.state.selectedIndex} - - - ); - } - - _onChange = event => { - this.setState({ - selectedIndex: event.nativeEvent.selectedSegmentIndex, - }); - }; - - _onValueChange = value => { - this.setState({ - value: value, - }); - }; -} - -const styles = StyleSheet.create({ - text: { - fontSize: 14, - textAlign: 'center', - fontWeight: '500', - margin: 10, - }, -}); +const { + BasicSegmentedControlExample, + PreSelectedSegmentedControlExample, + MomentarySegmentedControlExample, + DisabledSegmentedControlExample, + ColorSegmentedControlExample, + EventSegmentedControlExample, +} = require('./SegmentedControlExampleComponents'); exports.title = ''; exports.displayName = 'SegmentedControlExample'; diff --git a/RNTester/js/examples/Text/TextExample.android.js b/RNTester/js/examples/Text/TextExample.android.js index 72813a71cd97aa..f9631821f9900f 100644 --- a/RNTester/js/examples/Text/TextExample.android.js +++ b/RNTester/js/examples/Text/TextExample.android.js @@ -664,6 +664,9 @@ class TextExample extends React.Component<{...}> { + + + diff --git a/RNTester/js/examples/TextInput/TextInputExample.ios.js b/RNTester/js/examples/TextInput/TextInputExample.ios.js index 7e1673070e0109..311efa1179b066 100644 --- a/RNTester/js/examples/TextInput/TextInputExample.ios.js +++ b/RNTester/js/examples/TextInput/TextInputExample.ios.js @@ -707,4 +707,16 @@ exports.examples = ([ ); }, }, + { + title: 'showSoftInputOnFocus', + render: function(): React.Node { + return ( + + + + + + ); + }, + }, ]: Array); diff --git a/React-Core.podspec b/React-Core.podspec index 9acf10cbebf3fd..fc0a69f377561b 100644 --- a/React-Core.podspec +++ b/React-Core.podspec @@ -122,6 +122,7 @@ Pod::Spec.new do |s| s.dependency "RCT-Folly", folly_version s.dependency "React-cxxreact", version + s.dependency "React-perflogger", version s.dependency "React-jsi", version s.dependency "React-jsiexecutor", version s.dependency "Yoga" diff --git a/React/Base/RCTBundleURLProvider.h b/React/Base/RCTBundleURLProvider.h index 4254c9cae9b96e..cb9dc6f88a655e 100644 --- a/React/Base/RCTBundleURLProvider.h +++ b/React/Base/RCTBundleURLProvider.h @@ -39,9 +39,7 @@ extern NSString *const kRCTPlatformName; // TODO(macOS GH#774) */ - (NSString *)packagerServerHost; -#if RCT_DEV_MENU + (BOOL)isPackagerRunning:(NSString *)host; -#endif /** * Returns the jsBundleURL for a given bundle entrypoint and diff --git a/React/Base/RCTBundleURLProvider.m b/React/Base/RCTBundleURLProvider.m index e50b08fafc7a2d..c4a0684d4d41be 100644 --- a/React/Base/RCTBundleURLProvider.m +++ b/React/Base/RCTBundleURLProvider.m @@ -116,6 +116,11 @@ - (NSString *)guessPackagerHost } return nil; } +#else ++ (BOOL)isPackagerRunning:(NSString *)host +{ + return false; +} #endif - (NSString *)packagerServerHost @@ -194,6 +199,10 @@ + (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot kRCTPlatformName, // TODO(macOS GH#774) enableDev ? @"true" : @"false", enableMinification ? @"true" : @"false"]; + NSString *bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey]; + if (bundleID) { + query = [NSString stringWithFormat:@"%@&app=%@", query, bundleID]; + } return [[self class] resourceURLForResourcePath:path packagerHost:packagerHost query:query]; } diff --git a/React/Base/RCTJSInvokerModule.h b/React/Base/RCTJSInvokerModule.h index 4932c4703cabed..018c9b76f9508d 100644 --- a/React/Base/RCTJSInvokerModule.h +++ b/React/Base/RCTJSInvokerModule.h @@ -6,7 +6,7 @@ */ /** - * This protocol should be adopted when a turbo module needs to directly call into Javascript. + * This protocol should be adopted when a turbo module needs to directly call into JavaScript. * In bridge-less React Native, it is a replacement for [_bridge enqueueJSCall:]. */ @protocol RCTJSInvokerModule diff --git a/React/Base/RCTKeyCommands.h b/React/Base/RCTKeyCommands.h index 983348e9989212..2bdefcfab9dbed 100644 --- a/React/Base/RCTKeyCommands.h +++ b/React/Base/RCTKeyCommands.h @@ -12,37 +12,20 @@ + (instancetype)sharedInstance; /** - * Register a single-press keyboard command. + * Register a keyboard command. */ - (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *command))block; /** - * Unregister a single-press keyboard command. + * Unregister a keyboard command. */ - (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags; /** - * Check if a single-press command is registered. + * Check if a command is registered. */ - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags; -/** - * Register a double-press keyboard command. - */ -- (void)registerDoublePressKeyCommandWithInput:(NSString *)input - modifierFlags:(UIKeyModifierFlags)flags - action:(void (^)(UIKeyCommand *command))block; - -/** - * Unregister a double-press keyboard command. - */ -- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags; - -/** - * Check if a double-press command is registered. - */ -- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags; - @end diff --git a/React/Base/RCTKeyCommands.m b/React/Base/RCTKeyCommands.m index d48ba935d22f19..220a8b321f09cf 100644 --- a/React/Base/RCTKeyCommands.m +++ b/React/Base/RCTKeyCommands.m @@ -9,24 +9,38 @@ #import +#import +#import #import "RCTDefines.h" #import "RCTUtils.h" #if RCT_DEV +@interface UIEvent (UIPhysicalKeyboardEvent) + +@property (nonatomic) NSString *_modifiedInput; +@property (nonatomic) NSString *_unmodifiedInput; +@property (nonatomic) UIKeyModifierFlags _modifierFlags; +@property (nonatomic) BOOL _isKeyDown; +@property (nonatomic) long _keyCode; + +@end + @interface RCTKeyCommand : NSObject -@property (nonatomic, strong) UIKeyCommand *keyCommand; +@property (nonatomic, copy, readonly) NSString *key; +@property (nonatomic, readonly) UIKeyModifierFlags flags; @property (nonatomic, copy) void (^block)(UIKeyCommand *); @end @implementation RCTKeyCommand -- (instancetype)initWithKeyCommand:(UIKeyCommand *)keyCommand block:(void (^)(UIKeyCommand *))block +- (instancetype)init:(NSString *)key flags:(UIKeyModifierFlags)flags block:(void (^)(UIKeyCommand *))block { if ((self = [super init])) { - _keyCommand = keyCommand; + _key = key; + _flags = flags; _block = block; } return self; @@ -41,7 +55,7 @@ - (id)copyWithZone:(__unused NSZone *)zone - (NSUInteger)hash { - return _keyCommand.input.hash ^ _keyCommand.modifierFlags; + return _key.hash ^ _flags; } - (BOOL)isEqual:(RCTKeyCommand *)object @@ -49,12 +63,15 @@ - (BOOL)isEqual:(RCTKeyCommand *)object if (![object isKindOfClass:[RCTKeyCommand class]]) { return NO; } - return [self matchesInput:object.keyCommand.input flags:object.keyCommand.modifierFlags]; + return [self matchesInput:object.key flags:object.flags]; } - (BOOL)matchesInput:(NSString *)input flags:(UIKeyModifierFlags)flags { - return [_keyCommand.input isEqual:input] && _keyCommand.modifierFlags == flags; + // We consider the key command a match if the modifier flags match + // exactly or is there are no modifier flags. This means that for + // `cmd + r`, we will match both `cmd + r` and `r` but not `opt + r`. + return [_key isEqual:input] && (_flags == flags || flags == 0); } - (NSString *)description @@ -62,8 +79,8 @@ - (NSString *)description return [NSString stringWithFormat:@"<%@:%p input=\"%@\" flags=%lld hasBlock=%@>", [self class], self, - _keyCommand.input, - (long long)_keyCommand.modifierFlags, + _key, + (long long)_flags, _block ? @"YES" : @"NO"]; } @@ -75,115 +92,94 @@ @interface RCTKeyCommands () @end -@implementation UIResponder (RCTKeyCommands) +@implementation RCTKeyCommands -+ (UIResponder *)RCT_getFirstResponder:(UIResponder *)view ++ (void)initialize { - UIResponder *firstResponder = nil; + SEL originalKeyEventSelector = NSSelectorFromString(@"handleKeyUIEvent:"); + SEL swizzledKeyEventSelector = NSSelectorFromString( + [NSString stringWithFormat:@"_rct_swizzle_%x_%@", arc4random(), NSStringFromSelector(originalKeyEventSelector)]); - if (view.isFirstResponder) { - return view; - } else if ([view isKindOfClass:[UIViewController class]]) { - if ([(UIViewController *)view parentViewController]) { - firstResponder = [UIResponder RCT_getFirstResponder:[(UIViewController *)view parentViewController]]; - } - return firstResponder ? firstResponder : [UIResponder RCT_getFirstResponder:[(UIViewController *)view view]]; - } else if ([view isKindOfClass:[UIView class]]) { - for (UIView *subview in [(UIView *)view subviews]) { - firstResponder = [UIResponder RCT_getFirstResponder:subview]; - if (firstResponder) { - return firstResponder; - } - } - } + void (^handleKeyUIEventSwizzleBlock)(UIApplication *, UIEvent *) = ^(UIApplication *slf, UIEvent *event) { + [[[self class] sharedInstance] handleKeyUIEventSwizzle:event]; - return firstResponder; -} + ((void (*)(id, SEL, id))objc_msgSend)(slf, swizzledKeyEventSelector, event); + }; -- (NSArray *)RCT_keyCommands -{ - NSSet *commands = [RCTKeyCommands sharedInstance].commands; - return [[commands valueForKeyPath:@"keyCommand"] allObjects]; + RCTSwapInstanceMethodWithBlock( + [UIApplication class], originalKeyEventSelector, handleKeyUIEventSwizzleBlock, swizzledKeyEventSelector); } -/** - * Single Press Key Command Response - * Command + KeyEvent (Command + R/D, etc.) - */ -- (void)RCT_handleKeyCommand:(UIKeyCommand *)key +- (void)handleKeyUIEventSwizzle:(UIEvent *)event { - // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: - // method gets called repeatedly if the command key is held down. - static NSTimeInterval lastCommand = 0; - if (CACurrentMediaTime() - lastCommand > 0.5) { - for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { - if ([command.keyCommand.input isEqualToString:key.input] && - command.keyCommand.modifierFlags == key.modifierFlags) { - if (command.block) { - command.block(key); - lastCommand = CACurrentMediaTime(); - } - } - } + NSString *modifiedInput = nil; + UIKeyModifierFlags *modifierFlags = nil; + BOOL isKeyDown = NO; + + if ([event respondsToSelector:@selector(_modifiedInput)]) { + modifiedInput = [event _modifiedInput]; } -} -/** - * Double Press Key Command Response - * Double KeyEvent (Double R, etc.) - */ -- (void)RCT_handleDoublePressKeyCommand:(UIKeyCommand *)key -{ - static BOOL firstPress = YES; - static NSTimeInterval lastCommand = 0; - static NSTimeInterval lastDoubleCommand = 0; - static NSString *lastInput = nil; - static UIKeyModifierFlags lastModifierFlags = 0; - - if (firstPress) { - for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { - if ([command.keyCommand.input isEqualToString:key.input] && - command.keyCommand.modifierFlags == key.modifierFlags && command.block) { - firstPress = NO; - lastCommand = CACurrentMediaTime(); - lastInput = key.input; - lastModifierFlags = key.modifierFlags; - return; - } - } - } else { - // Second keyevent within 0.2 second, - // with the same key as the first one. - if (CACurrentMediaTime() - lastCommand < 0.2 && lastInput == key.input && lastModifierFlags == key.modifierFlags) { - for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { - if ([command.keyCommand.input isEqualToString:key.input] && - command.keyCommand.modifierFlags == key.modifierFlags && command.block) { - // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: - // method gets called repeatedly if the command key is held down. - if (CACurrentMediaTime() - lastDoubleCommand > 0.5) { - command.block(key); - lastDoubleCommand = CACurrentMediaTime(); - } - firstPress = YES; - return; - } + if ([event respondsToSelector:@selector(_modifierFlags)]) { + modifierFlags = [event _modifierFlags]; + } + + if ([event respondsToSelector:@selector(_isKeyDown)]) { + isKeyDown = [event _isKeyDown]; + } + + BOOL interactionEnabled = !UIApplication.sharedApplication.isIgnoringInteractionEvents; + BOOL hasFirstResponder = NO; + if (isKeyDown && modifiedInput.length > 0 && interactionEnabled) { + UIResponder *firstResponder = nil; + for (UIWindow *window in [self allWindows]) { + firstResponder = [window valueForKey:@"firstResponder"]; + if (firstResponder) { + hasFirstResponder = YES; + break; } } - lastCommand = CACurrentMediaTime(); - lastInput = key.input; - lastModifierFlags = key.modifierFlags; + // Ignore key commands (except escape) when there's an active responder + if (!firstResponder) { + [self RCT_handleKeyCommand:modifiedInput flags:modifierFlags]; + } } -} - -@end +}; -@implementation RCTKeyCommands +- (NSArray *)allWindows +{ + BOOL includeInternalWindows = YES; + BOOL onlyVisibleWindows = NO; + + // Obfuscating selector allWindowsIncludingInternalWindows:onlyVisibleWindows: + NSArray *allWindowsComponents = + @[ @"al", @"lWindo", @"wsIncl", @"udingInt", @"ernalWin", @"dows:o", @"nlyVisi", @"bleWin", @"dows:" ]; + SEL allWindowsSelector = NSSelectorFromString([allWindowsComponents componentsJoinedByString:@""]); + + NSMethodSignature *methodSignature = [[UIWindow class] methodSignatureForSelector:allWindowsSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + + invocation.target = [UIWindow class]; + invocation.selector = allWindowsSelector; + [invocation setArgument:&includeInternalWindows atIndex:2]; + [invocation setArgument:&onlyVisibleWindows atIndex:3]; + [invocation invoke]; + + __unsafe_unretained NSArray *windows = nil; + [invocation getReturnValue:&windows]; + return windows; +} -+ (void)initialize +- (void)RCT_handleKeyCommand:(NSString *)input flags:(UIKeyModifierFlags)modifierFlags { - // swizzle UIResponder - RCTSwapInstanceMethods([UIResponder class], @selector(keyCommands), @selector(RCT_keyCommands)); + for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { + if ([command matchesInput:input flags:modifierFlags]) { + if (command.block) { + command.block(nil); + } + } + } } + (instancetype)sharedInstance @@ -211,11 +207,7 @@ - (void)registerKeyCommandWithInput:(NSString *)input { RCTAssertMainQueue(); - UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input - modifierFlags:flags - action:@selector(RCT_handleKeyCommand:)]; - - RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] initWithKeyCommand:command block:block]; + RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] init:input flags:flags block:block]; [_commands removeObject:keyCommand]; [_commands addObject:keyCommand]; } @@ -244,45 +236,6 @@ - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyMod return NO; } -- (void)registerDoublePressKeyCommandWithInput:(NSString *)input - modifierFlags:(UIKeyModifierFlags)flags - action:(void (^)(UIKeyCommand *))block -{ - RCTAssertMainQueue(); - - UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input - modifierFlags:flags - action:@selector(RCT_handleDoublePressKeyCommand:)]; - - RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] initWithKeyCommand:command block:block]; - [_commands removeObject:keyCommand]; - [_commands addObject:keyCommand]; -} - -- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags -{ - RCTAssertMainQueue(); - - for (RCTKeyCommand *command in _commands.allObjects) { - if ([command matchesInput:input flags:flags]) { - [_commands removeObject:command]; - break; - } - } -} - -- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags -{ - RCTAssertMainQueue(); - - for (RCTKeyCommand *command in _commands) { - if ([command matchesInput:input flags:flags]) { - return YES; - } - } - return NO; -} - @end #else @@ -309,21 +262,6 @@ - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyMod return NO; } -- (void)registerDoublePressKeyCommandWithInput:(NSString *)input - modifierFlags:(UIKeyModifierFlags)flags - action:(void (^)(UIKeyCommand *))block -{ -} - -- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags -{ -} - -- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags -{ - return NO; -} - @end #endif diff --git a/React/Base/RCTModuleData.mm b/React/Base/RCTModuleData.mm index b4e48ae5e121bd..14251ee6d590e6 100644 --- a/React/Base/RCTModuleData.mm +++ b/React/Base/RCTModuleData.mm @@ -8,7 +8,10 @@ #import "RCTModuleData.h" #import -#include +#import +#import + +#import #import "RCTBridge+Private.h" #import "RCTBridge.h" @@ -17,6 +20,16 @@ #import "RCTProfile.h" #import "RCTUtils.h" +using namespace facebook::react; + +namespace { +int32_t getUniqueId() +{ + static std::atomic counter{0}; + return counter++; +} +} + @implementation RCTModuleData { NSDictionary *_constantsToExport; NSString *_queueName; @@ -110,22 +123,29 @@ - (instancetype)initWithModuleInstance:(id)instance bridge:(RCT #pragma mark - private setup methods -- (void)setUpInstanceAndBridge +- (void)setUpInstanceAndBridge:(int32_t)requestId { + NSString *moduleName = [self name]; + RCT_PROFILE_BEGIN_EVENT( RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge]", @{@"moduleClass" : NSStringFromClass(_moduleClass)}); { std::unique_lock lock(_instanceLock); + BOOL shouldSetup = !_setupComplete && _bridge.valid; - if (!_setupComplete && _bridge.valid) { + if (shouldSetup) { if (!_instance) { if (RCT_DEBUG && _requiresMainQueueSetup) { RCTAssertMainQueue(); } RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] Create module", nil); + + BridgeNativeModulePerfLogger::moduleCreateConstructStart([moduleName UTF8String], requestId); _instance = _moduleProvider ? _moduleProvider() : nil; + BridgeNativeModulePerfLogger::moduleCreateConstructEnd([moduleName UTF8String], requestId); + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); if (!_instance) { // Module init returned nil, probably because automatic instantiation @@ -143,7 +163,13 @@ - (void)setUpInstanceAndBridge if (_instance && RCTProfileIsProfiling()) { RCTProfileHookInstance(_instance); } + } + + if (_instance) { + BridgeNativeModulePerfLogger::moduleCreateSetUpStart([moduleName UTF8String], requestId); + } + if (shouldSetup) { // Bridge must be set before methodQueue is set up, as methodQueue // initialization requires it (View Managers get their queue by calling // self.bridge.uiManager.methodQueue) @@ -171,6 +197,10 @@ - (void)setUpInstanceAndBridge // thread. _requiresMainQueueSetup = NO; } + + if (_instance) { + BridgeNativeModulePerfLogger::moduleCreateSetUpEnd([moduleName UTF8String], requestId); + } } - (void)setBridgeForInstance @@ -286,6 +316,10 @@ - (BOOL)hasInstance - (id)instance { + NSString *moduleName = [self name]; + int32_t requestId = getUniqueId(); + BridgeNativeModulePerfLogger::moduleCreateStart([moduleName UTF8String], requestId); + if (!_setupComplete) { RCT_PROFILE_BEGIN_EVENT( RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData instanceForClass:%@]", _moduleClass]), nil); @@ -301,13 +335,21 @@ - (BOOL)hasInstance } RCTUnsafeExecuteOnMainQueueSync(^{ - [self setUpInstanceAndBridge]; + [self setUpInstanceAndBridge:requestId]; }); RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); } else { - [self setUpInstanceAndBridge]; + [self setUpInstanceAndBridge:requestId]; } RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); + } else { + BridgeNativeModulePerfLogger::moduleCreateCacheHit([moduleName UTF8String], requestId); + } + + if (_instance) { + BridgeNativeModulePerfLogger::moduleCreateEnd([moduleName UTF8String], requestId); + } else { + BridgeNativeModulePerfLogger::moduleCreateFail([moduleName UTF8String], requestId); } return _instance; } @@ -331,10 +373,22 @@ - (NSString *)name - (void)gatherConstants { + NSString *moduleName = [self name]; + if (_hasConstantsToExport && !_constantsToExport) { RCT_PROFILE_BEGIN_EVENT( RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass]), nil); (void)[self instance]; + + /** + * Why do we instrument moduleJSRequireEndingStart here? + * - NativeModule requires from JS go through ModuleRegistry::getConfig(). + * - ModuleRegistry::getConfig() calls NativeModule::getConstants() first. + * - This delegates to RCTNativeModule::getConstants(), which calls RCTModuleData gatherConstants(). + * - Therefore, this is the first statement that executes after the NativeModule is created/initialized in a JS + * require. + */ + BridgeNativeModulePerfLogger::moduleJSRequireEndingStart([moduleName UTF8String]); if (_requiresMainQueueSetup) { if (!RCTIsMainQueue()) { RCTLogWarn(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass); @@ -347,6 +401,12 @@ - (void)gatherConstants _constantsToExport = [_instance constantsToExport] ?: @{}; } RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); + } else { + /** + * If a NativeModule doesn't have constants, it isn't eagerly loaded until its methods are first invoked. + * Therefore, we should immediately start JSRequireEnding + */ + BridgeNativeModulePerfLogger::moduleJSRequireEndingStart([moduleName UTF8String]); } } diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h index e69f8f2c5f8907..2595fbcf6659f9 100644 --- a/React/Base/RCTUtils.h +++ b/React/Base/RCTUtils.h @@ -61,6 +61,7 @@ RCT_EXTERN CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale); // Method swizzling RCT_EXTERN IMP RCTSwapClassMethods(Class cls, SEL original, SEL replacement); // TODO(OSS Candidate ISS#2710739) RCT_EXTERN IMP RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement); // TODO(OSS Candidate ISS#2710739) +RCT_EXTERN void RCTSwapInstanceMethodWithBlock(Class cls, SEL original, id replacementBlock, SEL replacementSelector); // Module subclass support RCT_EXTERN BOOL RCTClassOverridesClassMethod(Class cls, SEL selector); diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 953bed5855c28b..beee44e90ed135 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -404,6 +404,19 @@ IMP RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement) // TODO(OSS return originalImplementation; // TODO(OSS Candidate ISS#2710739) } +void RCTSwapInstanceMethodWithBlock(Class cls, SEL original, id replacementBlock, SEL replacementSelector) +{ + Method originalMethod = class_getInstanceMethod(cls, original); + if (!originalMethod) { + return; + } + + IMP implementation = imp_implementationWithBlock(replacementBlock); + class_addMethod(cls, replacementSelector, implementation, method_getTypeEncoding(originalMethod)); + Method newMethod = class_getInstanceMethod(cls, replacementSelector); + method_exchangeImplementations(originalMethod, newMethod); +} + BOOL RCTClassOverridesClassMethod(Class cls, SEL selector) { return RCTClassOverridesInstanceMethod(object_getClass(cls), selector); diff --git a/React/Base/RCTUtilsUIOverride.h b/React/Base/RCTUtilsUIOverride.h index d89dcbc14854e8..1ac2bc17c01311 100644 --- a/React/Base/RCTUtilsUIOverride.h +++ b/React/Base/RCTUtilsUIOverride.h @@ -5,6 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +#import +#import // TODO(macOS GH#774) + @interface RCTUtilsUIOverride : NSObject /** Set the global presented view controller instance override. diff --git a/React/CoreModules/BUCK b/React/CoreModules/BUCK index 155d6378fa3c09..274a89b115fdd6 100644 --- a/React/CoreModules/BUCK +++ b/React/CoreModules/BUCK @@ -17,6 +17,7 @@ rn_apple_library( compiler_flags = [ "-Wno-error=unguarded-availability-new", "-Wno-unknown-warning-option", + "-Wno-unused-property-ivar", ], contacts = ["oncall+react_native@xmail.facebook.com"], enable_exceptions = True, @@ -35,7 +36,7 @@ rn_apple_library( header_path_prefix = "React", labels = [ "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. - "supermodule:ios/default/public.react_native.infra", + "supermodule:xplat/default/public.react_native.infra", ], link_whole = True, platform_preprocessor_flags = [( diff --git a/React/CoreModules/RCTAppearance.h b/React/CoreModules/RCTAppearance.h index 1f91bbdf061eb8..03da22857ff469 100644 --- a/React/CoreModules/RCTAppearance.h +++ b/React/CoreModules/RCTAppearance.h @@ -12,6 +12,11 @@ RCT_EXTERN void RCTEnableAppearancePreference(BOOL enabled); RCT_EXTERN void RCTOverrideAppearancePreference(NSString *const); +#if !TARGET_OS_OSX // TODO(macOS GH#774) +RCT_EXTERN NSString *RCTColorSchemePreference(UITraitCollection *traitCollection); +#else // [TODO(macOS GH#774) +RCT_EXTERN NSString *RCTColorSchemePreference(NSAppearance *appearance); +#endif // ]TODO(macOS GH#774) @interface RCTAppearance : RCTEventEmitter @end diff --git a/React/CoreModules/RCTAppearance.mm b/React/CoreModules/RCTAppearance.mm index 36525f9563da39..b0da18b72db345 100644 --- a/React/CoreModules/RCTAppearance.mm +++ b/React/CoreModules/RCTAppearance.mm @@ -31,7 +31,7 @@ void RCTOverrideAppearancePreference(NSString *const colorSchemeOverride) } #if !TARGET_OS_OSX // TODO(macOS GH#774) -static NSString *RCTColorSchemePreference(UITraitCollection *traitCollection) +NSString *RCTColorSchemePreference(UITraitCollection *traitCollection) { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 @@ -64,7 +64,7 @@ void RCTOverrideAppearancePreference(NSString *const colorSchemeOverride) return RCTAppearanceColorSchemeLight; } #else // [TODO(macOS GH#774) -static NSString *RCTColorSchemePreference(NSAppearance *appearance) +NSString *RCTColorSchemePreference(NSAppearance *appearance) { if (@available(macOS 10.14, *)) { static NSDictionary *appearances; diff --git a/React/CoreModules/RCTDevLoadingView.mm b/React/CoreModules/RCTDevLoadingView.mm index da8c928f315da1..0a899170c080bd 100644 --- a/React/CoreModules/RCTDevLoadingView.mm +++ b/React/CoreModules/RCTDevLoadingView.mm @@ -10,6 +10,7 @@ #import #import +#import #import #import #import @@ -243,27 +244,65 @@ - (void)showMessage:(NSString *)message color:(RCTUIColor *)color backgroundColo }); } +- (void)showProgressMessage:(NSString *)message +{ + if (self->_window != nil) { + // This is an optimization. Since the progress can come in quickly, + // we want to do the minimum amount of work to update the UI, + // which is to only update the label text. +#if !TARGET_OS_OSX // TODO(macOS GH#774) + self->_label.text = message; +#else // [TODO(macOS GH#774) + self->_label.stringValue = message; +#endif // ]TODO(macOS GH#774) + return; + } + + RCTUIColor *color = [RCTUIColor whiteColor]; // TODO(macOS GH#774) + RCTUIColor *backgroundColor = [RCTUIColor colorWithHue:105 saturation:0 brightness:.25 alpha:1]; // TODO(macOS GH#774) + + if ([self isDarkModeEnabled]) { + color = [RCTUIColor colorWithHue:208 saturation:0.03 brightness:.14 alpha:1]; // TODO(macOS GH#774) + backgroundColor = [RCTUIColor colorWithHue:0 saturation:0 brightness:0.98 alpha:1]; // TODO(macOS GH#774) + } + + [self showMessage:message color:color backgroundColor:backgroundColor]; +} + +- (void)showOfflineMessage +{ + RCTUIColor *color = [RCTUIColor whiteColor]; // TODO(macOS GH#774) + RCTUIColor *backgroundColor = [RCTUIColor blackColor]; // TODO(macOS GH#774) + + if ([self isDarkModeEnabled]) { + color = [RCTUIColor blackColor]; // TODO(macOS GH#774) + backgroundColor = [RCTUIColor whiteColor]; // TODO(macOS GH#774) + } + + NSString *message = [NSString stringWithFormat:@"Connect to %@ to develop JavaScript.", RCT_PACKAGER_NAME]; + [self showMessage:message color:color backgroundColor:backgroundColor]; +} + +- (BOOL)isDarkModeEnabled +{ + // We pass nil here to match the behavior of the native module. + // If we were to pass a view, then it's possible that this native + // banner would have a different color than the JavaScript banner + // (which always passes nil). This would result in an inconsistent UI. + return [RCTColorSchemePreference(nil) isEqualToString:@"dark"]; +} - (void)showWithURL:(NSURL *)URL { - RCTUIColor *color; // TODO(macOS GH#774) - RCTUIColor *backgroundColor; // TODO(macOS GH#774) - NSString *message; if (URL.fileURL) { // If dev mode is not enabled, we don't want to show this kind of notification. #if !RCT_DEV return; #endif - color = [RCTUIColor whiteColor]; //TODO(OSS Candidate ISS#2710739) UIColor -> RCTUIColor - backgroundColor = [RCTUIColor blackColor]; // TODO(OSS Candidate ISS#2710739) - message = [NSString stringWithFormat:@"Connect to %@ to develop JavaScript.", RCT_PACKAGER_NAME]; - [self showMessage:message color:color backgroundColor:backgroundColor]; + [self showOfflineMessage]; } else { - color = [RCTUIColor whiteColor]; // TODO(OSS Candidate ISS#2710739) - backgroundColor = [RCTUIColor colorWithHue:105 saturation:0 brightness:.25 alpha:1]; // TODO(OSS Candidate ISS#2710739) - message = [NSString stringWithFormat:@"Loading from %@\u2026", RCT_PACKAGER_NAME]; - [self showInitialMessageDelayed:^{ - [self showMessage:message color:color backgroundColor:backgroundColor]; + NSString *message = [NSString stringWithFormat:@"Loading from %@\u2026", RCT_PACKAGER_NAME]; + [self showProgressMessage:message]; }]; } } @@ -278,22 +317,7 @@ - (void)updateProgress:(RCTLoadingProgress *)progress [self clearInitialMessageDelay]; dispatch_async(dispatch_get_main_queue(), ^{ - if (self->_window == nil) { - // If we didn't show the initial message, then there's no banner window. - // We need to create it here so that the progress is actually displayed. - RCTUIColor *color = [RCTUIColor whiteColor]; - RCTUIColor *backgroundColor = [RCTUIColor colorWithHue:105 saturation:0 brightness:.25 alpha:1]; - [self showMessage:[progress description] color:color backgroundColor:backgroundColor]; - } else { - // This is an optimization. Since the progress can come in quickly, - // we want to do the minimum amount of work to update the UI, - // which is to only update the label text. -#if !TARGET_OS_OSX // TODO(macOS GH#774) - self->_label.text = [progress description]; -#else // [TODO(macOS GH#774) - self->_label.stringValue = [progress description]; -#endif // ]TODO(macOS GH#774) - } + [self showProgressMessage:[progress description]]; }); } diff --git a/React/CoreModules/RCTExceptionsManager.mm b/React/CoreModules/RCTExceptionsManager.mm index 4899d7af7cd78f..072e4511f2d813 100644 --- a/React/CoreModules/RCTExceptionsManager.mm +++ b/React/CoreModules/RCTExceptionsManager.mm @@ -24,6 +24,7 @@ @interface RCTExceptionsManager () @implementation RCTExceptionsManager @synthesize bridge = _bridge; +@synthesize turboModuleLookupDelegate = _turboModuleLookupDelegate; RCT_EXPORT_MODULE() @@ -41,7 +42,13 @@ - (void)reportSoft:(NSString *)message suppressRedBox:(BOOL)suppressRedBox { if (!suppressRedBox) { - [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)]; + // TODO T5287269 - Delete _bridge case when TM ships. + if (_bridge) { + [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)]; + } else { + RCTRedBox *redbox = [_turboModuleLookupDelegate moduleForName:"RCTRedBox"]; + [redbox showErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; + } } if (_delegate) { @@ -57,7 +64,13 @@ - (void)reportFatal:(NSString *)message suppressRedBox:(BOOL)suppressRedBox { if (!suppressRedBox) { - [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)]; + // TODO T5287269 - Delete _bridge case when TM ships. + if (_bridge) { + [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)]; + } else { + RCTRedBox *redbox = [_turboModuleLookupDelegate moduleForName:"RCTRedBox"]; + [redbox showErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; + } } if (_delegate) { @@ -98,7 +111,13 @@ - (void)reportFatal:(NSString *)message : (NSArray *)stack exceptionId : (double)exceptionId) { - [_bridge.redBox updateErrorMessage:message withStack:stack errorCookie:((int)exceptionId)]; + // TODO T5287269 - Delete _bridge case when TM ships. + if (_bridge) { + [_bridge.redBox updateErrorMessage:message withStack:stack errorCookie:((int)exceptionId)]; + } else { + RCTRedBox *redbox = [_turboModuleLookupDelegate moduleForName:"RCTRedBox"]; + [redbox updateErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; + } if (_delegate && [_delegate respondsToSelector:@selector(updateJSExceptionWithMessage:stack:exceptionId:)]) { [_delegate updateJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]]; diff --git a/React/CoreModules/RCTLogBox.h b/React/CoreModules/RCTLogBox.h index 3aaa8c97ee04f0..f81805d2549ffd 100644 --- a/React/CoreModules/RCTLogBox.h +++ b/React/CoreModules/RCTLogBox.h @@ -7,15 +7,6 @@ #import // TODO(macOS GH#774) -#import -#import -#import - -@class RCTJSStackFrame; - -@interface RCTLogBox : NSObject - -- (void)show; -- (void)hide; +@interface RCTLogBox : NSObject @end diff --git a/React/CoreModules/RCTLogBox.mm b/React/CoreModules/RCTLogBox.mm index 1693cc6fb31c95..ebb57011052995 100644 --- a/React/CoreModules/RCTLogBox.mm +++ b/React/CoreModules/RCTLogBox.mm @@ -9,18 +9,10 @@ #import #import -#import -#import -#import -#import -#import +#import +#import #import -#import -#import #import -#import - -#import #import "CoreModulesPlugins.h" @@ -139,7 +131,7 @@ - (void)_showModal #endif // ]TODO(macOS GH#774) -@interface RCTLogBox () +@interface RCTLogBox () @end @implementation RCTLogBox { diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index a996fda0b6fab9..b2501d62d64791 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -36,6 +36,7 @@ #import #import #import +#import #if TARGET_OS_OSX && __has_include() #define RCT_USE_HERMES 1 @@ -78,6 +79,12 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { namespace { +int32_t getUniqueId() +{ + static std::atomic counter{0}; + return counter++; +} + class GetDescAdapter : public JSExecutorFactory { public: GetDescAdapter(RCTCxxBridge *bridge, std::shared_ptr factory) : bridge_(bridge), factory_(factory) @@ -258,24 +265,10 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge _moduleDataByID = [NSMutableArray new]; [RCTBridge setCurrentBridge:self]; - -#if !TARGET_OS_OSX // TODO(macOS GH#774) - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleMemoryWarning) - name:UIApplicationDidReceiveMemoryWarningNotification - object:nil]; -#endif // TODO(macOS GH#774) } return self; } -#if !TARGET_OS_OSX // TODO(macOS GH#774) -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} -#endif // TODO(macOS GH#774) - + (void)runRunLoop { @autoreleasepool { @@ -309,13 +302,6 @@ - (void)_tryAndHandleError:(dispatch_block_t)block } } -- (void)handleMemoryWarning -{ - if (_reactInstance) { - _reactInstance->handleMemoryPressure(15 /* TRIM_MEMORY_RUNNING_CRITICAL */); - } -} - /** * Ensure block is run on the JS thread. If we're already on the JS thread, the block will execute synchronously. * If we're not on the JS thread, the block is dispatched to that thread. Any errors encountered while executing @@ -702,7 +688,10 @@ - (void)updateModuleWithInstance:(id)instance // Instantiate moduleData // TODO #13258411: can we defer this until config generation? + int32_t moduleDataId = getUniqueId(); + BridgeNativeModulePerfLogger::moduleDataCreateStart([moduleName UTF8String], moduleDataId); moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self]; + BridgeNativeModulePerfLogger::moduleDataCreateEnd([moduleName UTF8String], moduleDataId); _moduleDataByName[moduleName] = moduleData; [_moduleClassesByID addObject:moduleClass]; @@ -762,7 +751,11 @@ - (void)registerExtraModules } // Instantiate moduleData container + int32_t moduleDataId = getUniqueId(); + BridgeNativeModulePerfLogger::moduleDataCreateStart([moduleName UTF8String], moduleDataId); RCTModuleData *moduleData = [[RCTModuleData alloc] initWithModuleInstance:module bridge:self]; + BridgeNativeModulePerfLogger::moduleDataCreateEnd([moduleName UTF8String], moduleDataId); + _moduleDataByName[moduleName] = moduleData; [_moduleClassesByID addObject:moduleClass]; [_moduleDataByID addObject:moduleData]; @@ -809,7 +802,10 @@ - (void)registerExtraLazyModules } } + int32_t moduleDataId = getUniqueId(); + BridgeNativeModulePerfLogger::moduleDataCreateStart([moduleName UTF8String], moduleDataId); moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self]; + BridgeNativeModulePerfLogger::moduleDataCreateEnd([moduleName UTF8String], moduleDataId); _moduleDataByName[moduleName] = moduleData; [_moduleClassesByID addObject:moduleClass]; diff --git a/React/CxxModule/RCTNativeModule.h b/React/CxxModule/RCTNativeModule.h index c73cf5f4ab7e8d..a59f9ae48181a9 100644 --- a/React/CxxModule/RCTNativeModule.h +++ b/React/CxxModule/RCTNativeModule.h @@ -16,6 +16,7 @@ class RCTNativeModule : public NativeModule { RCTNativeModule(RCTBridge *bridge, RCTModuleData *moduleData); std::string getName() override; + std::string getSyncMethodName(unsigned int methodId) override; std::vector getMethods() override; folly::dynamic getConstants() override; void invoke(unsigned int methodId, folly::dynamic &¶ms, int callId) diff --git a/React/CxxModule/RCTNativeModule.mm b/React/CxxModule/RCTNativeModule.mm index 00737371518d77..be74794fc572c0 100644 --- a/React/CxxModule/RCTNativeModule.mm +++ b/React/CxxModule/RCTNativeModule.mm @@ -15,16 +15,26 @@ #import #import #import +#import #ifdef WITH_FBSYSTRACE #include #endif +namespace { +enum SchedulingContext { Sync, Async }; +} + namespace facebook { namespace react { -static MethodCallResult -invokeInner(RCTBridge *bridge, RCTModuleData *moduleData, unsigned int methodId, const folly::dynamic ¶ms); +static MethodCallResult invokeInner( + RCTBridge *bridge, + RCTModuleData *moduleData, + unsigned int methodId, + const folly::dynamic ¶ms, + int callId, + SchedulingContext context); RCTNativeModule::RCTNativeModule(RCTBridge *bridge, RCTModuleData *moduleData) : m_bridge(bridge), m_moduleData(moduleData) @@ -36,6 +46,11 @@ return [m_moduleData.name UTF8String]; } +std::string RCTNativeModule::getSyncMethodName(unsigned int methodId) +{ + return m_moduleData.methods[methodId].JSMethodName; +} + std::vector RCTNativeModule::getMethods() { std::vector descs; @@ -58,13 +73,26 @@ void RCTNativeModule::invoke(unsigned int methodId, folly::dynamic &¶ms, int callId) { + const char *moduleName = [m_moduleData.name UTF8String]; + const char *methodName = m_moduleData.methods[methodId].JSMethodName; + + dispatch_queue_t queue = m_moduleData.methodQueue; + const bool isSyncModule = queue == RCTJSThread; + + if (isSyncModule) { + BridgeNativeModulePerfLogger::syncMethodCallStart(moduleName, methodName); + BridgeNativeModulePerfLogger::syncMethodCallArgConversionStart(moduleName, methodName); + } else { + BridgeNativeModulePerfLogger::asyncMethodCallStart(moduleName, methodName); + } + // capture by weak pointer so that we can safely use these variables in a callback __weak RCTBridge *weakBridge = m_bridge; __weak RCTModuleData *weakModuleData = m_moduleData; // The BatchedBridge version of this buckets all the callbacks by thread, and // queues one block on each. This is much simpler; we'll see how it goes and // iterate. - dispatch_block_t block = [weakBridge, weakModuleData, methodId, params = std::move(params), callId] { + dispatch_block_t block = [weakBridge, weakModuleData, methodId, params = std::move(params), callId, isSyncModule] { #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); @@ -72,13 +100,14 @@ #else (void)(callId); #endif - invokeInner(weakBridge, weakModuleData, methodId, std::move(params)); + invokeInner(weakBridge, weakModuleData, methodId, std::move(params), callId, isSyncModule ? Sync : Async); }; - dispatch_queue_t queue = m_moduleData.methodQueue; - if (queue == RCTJSThread) { + if (isSyncModule) { block(); + BridgeNativeModulePerfLogger::syncMethodCallReturnConversionEnd(moduleName, methodName); } else if (queue) { + BridgeNativeModulePerfLogger::asyncMethodCallDispatch(moduleName, methodName); dispatch_async(queue, block); } @@ -90,17 +119,36 @@ m_moduleData.name); } #endif + + if (isSyncModule) { + BridgeNativeModulePerfLogger::syncMethodCallEnd(moduleName, methodName); + } else { + BridgeNativeModulePerfLogger::asyncMethodCallEnd(moduleName, methodName); + } } MethodCallResult RCTNativeModule::callSerializableNativeHook(unsigned int reactMethodId, folly::dynamic &¶ms) { - return invokeInner(m_bridge, m_moduleData, reactMethodId, params); + return invokeInner(m_bridge, m_moduleData, reactMethodId, params, 0, Sync); } -static MethodCallResult -invokeInner(RCTBridge *bridge, RCTModuleData *moduleData, unsigned int methodId, const folly::dynamic ¶ms) +static MethodCallResult invokeInner( + RCTBridge *bridge, + RCTModuleData *moduleData, + unsigned int methodId, + const folly::dynamic ¶ms, + int callId, + SchedulingContext context) { if (!bridge || !bridge.valid || !moduleData) { + if (context == Sync) { + /** + * NOTE: moduleName and methodName are "". This shouldn't be an issue because there can only be one ongoing sync + * call at a time, and when we call syncMethodCallFail, that one call should terminate. This is also an + * exceptional scenario, so it shouldn't occur often. + */ + BridgeNativeModulePerfLogger::syncMethodCallFail("N/A", "N/A"); + } return folly::none; } @@ -109,11 +157,44 @@ RCTLogError(@"Unknown methodID: %ud for module: %@", methodId, moduleData.name); } + const char *moduleName = [moduleData.name UTF8String]; + const char *methodName = moduleData.methods[methodId].JSMethodName; + + if (context == Async) { + BridgeNativeModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodName, (int32_t)callId); + BridgeNativeModulePerfLogger::asyncMethodCallExecutionArgConversionStart(moduleName, methodName, (int32_t)callId); + } + NSArray *objcParams = convertFollyDynamicToId(params); + + if (context == Sync) { + BridgeNativeModulePerfLogger::syncMethodCallArgConversionEnd(moduleName, methodName); + } + @try { + if (context == Sync) { + BridgeNativeModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName); + } else { + BridgeNativeModulePerfLogger::asyncMethodCallExecutionArgConversionEnd(moduleName, methodName, (int32_t)callId); + } + id result = [method invokeWithBridge:bridge module:moduleData.instance arguments:objcParams]; + + if (context == Sync) { + BridgeNativeModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodName); + BridgeNativeModulePerfLogger::syncMethodCallReturnConversionStart(moduleName, methodName); + } else { + BridgeNativeModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodName, (int32_t)callId); + } + return convertIdToFollyDynamic(result); } @catch (NSException *exception) { + if (context == Sync) { + BridgeNativeModulePerfLogger::syncMethodCallFail(moduleName, methodName); + } else { + BridgeNativeModulePerfLogger::asyncMethodCallExecutionFail(moduleName, methodName, (int32_t)callId); + } + // Pass on JS exceptions if ([exception.name hasPrefix:RCTFatalExceptionName]) { @throw exception; diff --git a/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm b/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm index 9da0483fad089a..afd1873bacfe4d 100644 --- a/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm @@ -15,8 +15,11 @@ using namespace facebook::react; +static NSString *const kRCTLegacyInteropChildComponentKey = @"childComponentView"; +static NSString *const kRCTLegacyInteropChildIndexKey = @"index"; + @implementation RCTLegacyViewManagerInteropComponentView { - NSMutableDictionary *_viewsToBeMounted; + NSMutableArray *_viewsToBeMounted; NSMutableArray *_viewsToBeUnmounted; RCTLegacyViewManagerInteropCoordinatorAdapter *_adapter; LegacyViewManagerInteropShadowNode::ConcreteState::Shared _state; @@ -27,7 +30,7 @@ - (instancetype)initWithFrame:(CGRect)frame if (self = [super initWithFrame:frame]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; - _viewsToBeMounted = [NSMutableDictionary new]; + _viewsToBeMounted = [NSMutableArray new]; _viewsToBeUnmounted = [NSMutableArray new]; } @@ -89,7 +92,10 @@ - (void)prepareForRecycle - (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index { - [_viewsToBeMounted setObject:childComponentView forKey:[NSNumber numberWithInteger:index]]; + [_viewsToBeMounted addObject:@{ + kRCTLegacyInteropChildIndexKey : [NSNumber numberWithInteger:index], + kRCTLegacyInteropChildComponentKey : childComponentView + }]; } - (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index @@ -126,8 +132,10 @@ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask self.contentView = _adapter.paperView; } - for (NSNumber *key in _viewsToBeMounted) { - [_adapter.paperView insertReactSubview:_viewsToBeMounted[key] atIndex:key.integerValue]; + for (NSDictionary *mountInstruction in _viewsToBeMounted) { + NSNumber *index = mountInstruction[kRCTLegacyInteropChildIndexKey]; + UIView *childView = mountInstruction[kRCTLegacyInteropChildComponentKey]; + [_adapter.paperView insertReactSubview:childView atIndex:index.integerValue]; } [_viewsToBeMounted removeAllObjects]; diff --git a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h index 60f9dca7efafd3..7b002cbf8bf66f 100644 --- a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h +++ b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h @@ -38,6 +38,9 @@ Class RCTSwitchCls(void) __attribute__((used)); Class RCTUnimplementedNativeViewCls(void) __attribute__((used)); Class RCTModalHostViewCls(void) __attribute__((used)); Class RCTImageCls(void) __attribute__((used)); +Class RCTParagraphCls(void) __attribute__((used)); +Class RCTTextInputCls(void) __attribute__((used)); +Class RCTViewCls(void) __attribute__((used)); #ifdef __cplusplus } diff --git a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm index f56ec907d8467a..33cc39126fe494 100644 --- a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm +++ b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm @@ -27,6 +27,9 @@ {"UnimplementedNativeView", RCTUnimplementedNativeViewCls}, {"ModalHostView", RCTModalHostViewCls}, {"Image", RCTImageCls}, + {"Paragraph", RCTParagraphCls}, + {"TextInput", RCTTextInputCls}, + {"View", RCTViewCls}, }; auto p = sFabricComponentsClassMap.find(name); diff --git a/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm index 982f63d5037a1a..6886660a73fd74 100644 --- a/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm @@ -18,6 +18,7 @@ @implementation RCTSafeAreaViewComponentView { SafeAreaViewShadowNode::ConcreteState::Shared _state; + EdgeInsets _lastPaddingStateWasUpdatedWith; } - (instancetype)initWithFrame:(CGRect)frame @@ -40,11 +41,6 @@ - (UIEdgeInsets)_safeAreaInsets return UIEdgeInsetsZero; } -- (void)layoutSubviews -{ - [super layoutSubviews]; -} - - (void)safeAreaInsetsDidChange { [super safeAreaInsetsDidChange]; @@ -64,16 +60,16 @@ - (void)_updateStateIfNecessary insets.right = RCTRoundPixelValue(insets.right); insets.bottom = RCTRoundPixelValue(insets.bottom); - auto oldPadding = _state->getData().padding; auto newPadding = RCTEdgeInsetsFromUIEdgeInsets(insets); auto threshold = 1.0 / RCTScreenScale() + 0.01; // Size of a pixel plus some small threshold. - auto deltaPadding = newPadding - oldPadding; + auto deltaPadding = newPadding - _lastPaddingStateWasUpdatedWith; if (std::abs(deltaPadding.left) < threshold && std::abs(deltaPadding.top) < threshold && std::abs(deltaPadding.right) < threshold && std::abs(deltaPadding.bottom) < threshold) { return; } + _lastPaddingStateWasUpdatedWith = newPadding; _state->updateState(SafeAreaViewState{newPadding}); } @@ -90,6 +86,7 @@ - (void)prepareForRecycle { [super prepareForRecycle]; _state.reset(); + _lastPaddingStateWasUpdatedWith = {}; } + (ComponentDescriptorProvider)componentDescriptorProvider diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm index 33ff40c52e9060..cf36aafb33d4f3 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm @@ -6,6 +6,7 @@ */ #import "RCTEnhancedScrollView.h" +#import @implementation RCTEnhancedScrollView { __weak id _publicDelegate; @@ -84,4 +85,27 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } +/* + * Automatically centers the content such that if the content is smaller than the + * ScrollView, we force it to be centered, but when you zoom or the content otherwise + * becomes larger than the ScrollView, there is no padding around the content but it + * can still fill the whole view. + */ +- (void)setContentOffset:(CGPoint)contentOffset +{ + if (_centerContent && !CGSizeEqualToSize(self.contentSize, CGSizeZero)) { + CGSize scrollViewSize = self.bounds.size; + if (self.contentSize.width <= scrollViewSize.width) { + contentOffset.x = -(scrollViewSize.width - self.contentSize.width) / 2.0; + } + if (self.contentSize.height <= scrollViewSize.height) { + contentOffset.y = -(scrollViewSize.height - self.contentSize.height) / 2.0; + } + } + + super.contentOffset = CGPointMake( + RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"), + RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); +} + @end diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 7a22c36c032eee..4868e6b5121576 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -163,6 +163,10 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & _scrollView.contentInset = RCTUIEdgeInsetsFromEdgeInsets(newScrollViewProps.contentInset); } + if (oldScrollViewProps.contentOffset != newScrollViewProps.contentOffset) { + _scrollView.contentOffset = RCTCGPointFromPoint(newScrollViewProps.contentOffset); + } + // MAP_SCROLL_VIEW_PROP(scrollIndicatorInsets); // MAP_SCROLL_VIEW_PROP(snapToInterval); // MAP_SCROLL_VIEW_PROP(snapToAlignment); @@ -223,7 +227,8 @@ - (void)_updateStateWithContentOffset - (void)prepareForRecycle { - _scrollView.contentOffset = CGPointZero; + const auto &props = *std::static_pointer_cast(_props); + _scrollView.contentOffset = RCTCGPointFromPoint(props.contentOffset); _state.reset(); _isUserTriggeredScrolling = NO; [super prepareForRecycle]; diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index faaeb73736d7b3..42fa2ebe86ac2d 100644 --- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -19,6 +19,7 @@ #import #import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" using namespace facebook::react; @@ -169,3 +170,8 @@ - (SharedTouchEventEmitter)touchEventEmitterAtPoint:(CGPoint)point } @end + +Class RCTParagraphCls(void) +{ + return RCTParagraphComponentView.class; +} diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 1e7b92a2d104bb..8d936feba22625 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -20,6 +20,8 @@ #import "RCTTextInputNativeCommands.h" #import "RCTTextInputUtils.h" +#import "RCTFabricComponentsPlugins.h" + using namespace facebook::react; @interface RCTTextInputComponentView () @@ -62,7 +64,6 @@ - (instancetype)initWithFrame:(CGRect)frame auto &props = *defaultProps; _backedTextInputView = props.traits.multiline ? [[RCTUITextView alloc] init] : [[RCTUITextField alloc] init]; - _backedTextInputView.frame = self.bounds; _backedTextInputView.textInputDelegate = self; _ignoreNextTextInputCall = NO; _comingFromJS = NO; @@ -230,7 +231,7 @@ - (void)_setAttributedString:(NSAttributedString *)attributedString - (void)prepareForRecycle { [super prepareForRecycle]; - _backedTextInputView.attributedText = [[NSAttributedString alloc] init]; + _backedTextInputView.attributedText = nil; _mostRecentEventCount = 0; _state.reset(); _comingFromJS = NO; @@ -263,7 +264,7 @@ - (void)textInputDidBeginEditing auto const &props = *std::static_pointer_cast(_props); if (props.traits.clearTextOnFocus) { - _backedTextInputView.attributedText = [NSAttributedString new]; + _backedTextInputView.attributedText = nil; [self textInputDidChange]; } @@ -388,11 +389,10 @@ - (TextInputMetrics)_textInputMetrics - (void)_updateState { - NSAttributedString *attributedString = _backedTextInputView.attributedText; - if (!_state) { return; } + NSAttributedString *attributedString = _backedTextInputView.attributedText; auto data = _state->getData(); _lastStringStateWasUpdatedWith = attributedString; data.attributedStringBox = RCTAttributedStringBoxFromNSAttributedString(attributedString); @@ -459,3 +459,8 @@ - (void)setTextAndSelection:(NSInteger)eventCount } @end + +Class RCTTextInputCls(void) +{ + return RCTTextInputComponentView.class; +} diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index a0f8d7af169061..09bc95cb33d57d 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -15,6 +15,7 @@ #import #import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" using namespace facebook::react; @@ -179,7 +180,10 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `hitSlop` if (oldViewProps.hitSlop != newViewProps.hitSlop) { - self.hitTestEdgeInsets = RCTUIEdgeInsetsFromEdgeInsets(newViewProps.hitSlop); + self.hitTestEdgeInsets = {-newViewProps.hitSlop.top, + -newViewProps.hitSlop.left, + -newViewProps.hitSlop.bottom, + -newViewProps.hitSlop.right}; } // `overflow` @@ -518,7 +522,7 @@ - (NSString *)accessibilityLabel { auto const &accessibilityActions = _props->accessibilityActions; - if (accessibilityActions.size() == 0) { + if (accessibilityActions.empty()) { return nil; } @@ -584,3 +588,8 @@ - (NSString *)componentViewName_DO_NOT_USE_THIS_IS_BROKEN } @end + +Class RCTViewCls(void) +{ + return RCTViewComponentView.class; +} diff --git a/React/Fabric/Mounting/RCTComponentViewRegistry.mm b/React/Fabric/Mounting/RCTComponentViewRegistry.mm index 23d08cef831f64..b0dbe553b03ff4 100644 --- a/React/Fabric/Mounting/RCTComponentViewRegistry.mm +++ b/React/Fabric/Mounting/RCTComponentViewRegistry.mm @@ -189,7 +189,7 @@ - (RCTComponentViewDescriptor)_dequeueComponentViewWithComponentHandle:(Componen RCTAssertMainQueue(); auto &recycledViews = _recyclePool[componentHandle]; - if (recycledViews.size() == 0) { + if (recycledViews.empty()) { return [self.componentViewFactory createComponentViewWithComponentHandle:componentHandle]; } diff --git a/React/Fabric/Mounting/RCTMountingManager.h b/React/Fabric/Mounting/RCTMountingManager.h index eb4ca669c7a9f1..0e8771b8a8b907 100644 --- a/React/Fabric/Mounting/RCTMountingManager.h +++ b/React/Fabric/Mounting/RCTMountingManager.h @@ -26,8 +26,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) id delegate; @property (nonatomic, strong) RCTComponentViewRegistry *componentViewRegistry; -@property (atomic, assign) BOOL useModernDifferentiatorMode; - /** * Schedule a mounting transaction to be performed on the main thread. * Can be called from any thread. diff --git a/React/Fabric/Mounting/RCTMountingManager.mm b/React/Fabric/Mounting/RCTMountingManager.mm index 1aa7786d8591df..a64b1dd79ddbf0 100644 --- a/React/Fabric/Mounting/RCTMountingManager.mm +++ b/React/Fabric/Mounting/RCTMountingManager.mm @@ -266,10 +266,7 @@ - (void)performTransaction:(MountingCoordinator::Shared const &)mountingCoordina SystraceSection s("-[RCTMountingManager performTransaction:]"); RCTAssertMainQueue(); - auto differentiatorMode = - self.useModernDifferentiatorMode ? DifferentiatorMode::OptimizedMoves : DifferentiatorMode::Classic; - - auto transaction = mountingCoordinator->pullTransaction(differentiatorMode); + auto transaction = mountingCoordinator->pullTransaction(); if (!transaction.has_value()) { return; } @@ -277,7 +274,7 @@ - (void)performTransaction:(MountingCoordinator::Shared const &)mountingCoordina auto surfaceId = transaction->getSurfaceId(); auto &mutations = transaction->getMutations(); - if (mutations.size() == 0) { + if (mutations.empty()) { return; } diff --git a/React/Fabric/RCTScheduler.h b/React/Fabric/RCTScheduler.h index d38465591fe2c4..f85174c03f3877 100644 --- a/React/Fabric/RCTScheduler.h +++ b/React/Fabric/RCTScheduler.h @@ -64,6 +64,12 @@ NS_ASSUME_NONNULL_BEGIN - (facebook::react::MountingCoordinator::Shared)mountingCoordinatorWithSurfaceId:(facebook::react::SurfaceId)surfaceId; +- (void)onAnimationStarted; + +- (void)onAllAnimationsComplete; + +- (void)animationTick; + @end NS_ASSUME_NONNULL_END diff --git a/React/Fabric/RCTScheduler.mm b/React/Fabric/RCTScheduler.mm index 25821a72e6d913..d89af23b385638 100644 --- a/React/Fabric/RCTScheduler.mm +++ b/React/Fabric/RCTScheduler.mm @@ -7,10 +7,12 @@ #import "RCTScheduler.h" +#import #import #import #import #import +#include #import @@ -61,23 +63,82 @@ void schedulerDidClearJSResponder() override void *scheduler_; }; +class LayoutAnimationDelegateProxy : public LayoutAnimationStatusDelegate, public RunLoopObserver::Delegate { + public: + LayoutAnimationDelegateProxy(void *scheduler) : scheduler_(scheduler) {} + virtual ~LayoutAnimationDelegateProxy() {} + + void onAnimationStarted() override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler onAnimationStarted]; + } + + /** + * Called when the LayoutAnimation engine completes all pending animations. + */ + void onAllAnimationsComplete() override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler onAllAnimationsComplete]; + } + + void activityDidChange(RunLoopObserver::Delegate const *delegate, RunLoopObserver::Activity activity) const + noexcept override + { + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler animationTick]; + } + + private: + void *scheduler_; +}; + @implementation RCTScheduler { std::shared_ptr _scheduler; + std::shared_ptr _animationDriver; std::shared_ptr _delegateProxy; + std::shared_ptr _layoutAnimationDelegateProxy; + RunLoopObserver::Unique _uiRunLoopObserver; + BOOL _layoutAnimationsEnabled; } - (instancetype)initWithToolbox:(facebook::react::SchedulerToolbox)toolbox { if (self = [super init]) { + auto reactNativeConfig = + toolbox.contextContainer->at>("ReactNativeConfig"); + _layoutAnimationsEnabled = reactNativeConfig->getBool("react_fabric:enabled_layout_animations_ios"); + _delegateProxy = std::make_shared((__bridge void *)self); - _scheduler = std::make_shared(toolbox, _delegateProxy.get()); + + if (_layoutAnimationsEnabled) { + _layoutAnimationDelegateProxy = std::make_shared((__bridge void *)self); + _animationDriver = std::make_unique(_layoutAnimationDelegateProxy.get()); + _uiRunLoopObserver = + toolbox.mainRunLoopObserverFactory(RunLoopObserver::Activity::BeforeWaiting, _layoutAnimationDelegateProxy); + _uiRunLoopObserver->setDelegate(_layoutAnimationDelegateProxy.get()); + } + + _scheduler = std::make_shared( + toolbox, (_animationDriver ? _animationDriver.get() : nullptr), _delegateProxy.get()); } return self; } +- (void)animationTick +{ + _scheduler->animationTick(); +} + - (void)dealloc { + if (_animationDriver) { + _animationDriver->setLayoutAnimationStatusDelegate(nullptr); + } + _animationDriver = nullptr; + _scheduler->setDelegate(nullptr); } @@ -90,7 +151,13 @@ - (void)startSurfaceWithSurfaceId:(SurfaceId)surfaceId SystraceSection s("-[RCTScheduler startSurfaceWithSurfaceId:...]"); auto props = convertIdToFollyDynamic(initialProps); - _scheduler->startSurface(surfaceId, RCTStringFromNSString(moduleName), props, layoutConstraints, layoutContext); + _scheduler->startSurface( + surfaceId, + RCTStringFromNSString(moduleName), + props, + layoutConstraints, + layoutContext, + (_animationDriver ? _animationDriver.get() : nullptr)); _scheduler->renderTemplateToSurface( surfaceId, props.getDefault("navigationConfig").getDefault("initialUITemplate", "").getString()); } @@ -127,4 +194,18 @@ - (ComponentDescriptor const *)findComponentDescriptorByHandle_DO_NOT_USE_THIS_I return _scheduler->findMountingCoordinator(surfaceId); } +- (void)onAnimationStarted +{ + if (_uiRunLoopObserver) { + _uiRunLoopObserver->enable(); + } +} + +- (void)onAllAnimationsComplete +{ + if (_uiRunLoopObserver) { + _uiRunLoopObserver->disable(); + } +} + @end diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index a199c0d866b31c..1f9cbc8d75de7e 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -28,11 +28,14 @@ #import #import #import +#import #import +#import #import #import #import "MainRunLoopEventBeat.h" +#import "PlatformRunLoopObserver.h" #import "RCTConversions.h" #import "RuntimeEventBeat.h" @@ -277,6 +280,8 @@ - (BOOL)resume - (RCTScheduler *)_createScheduler { + auto reactNativeConfig = _contextContainer->at>("ReactNativeConfig"); + auto componentRegistryFactory = [factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)]( EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) { @@ -290,24 +295,36 @@ - (RCTScheduler *)_createScheduler toolbox.contextContainer = _contextContainer; toolbox.componentRegistryFactory = componentRegistryFactory; toolbox.runtimeExecutor = runtimeExecutor; - - toolbox.synchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { - return std::make_unique(ownerBox, runtimeExecutor); + toolbox.mainRunLoopObserverFactory = [](RunLoopObserver::Activity activities, + RunLoopObserver::WeakOwner const &owner) { + return std::make_unique(activities, owner); }; - toolbox.asynchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { - return std::make_unique(ownerBox, runtimeExecutor); - }; + if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_run_loop_based_event_beat_ios")) { + toolbox.synchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { + auto runLoopObserver = + std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); + return std::make_unique(std::move(runLoopObserver), runtimeExecutor); + }; + + toolbox.asynchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { + auto runLoopObserver = + std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); + return std::make_unique(std::move(runLoopObserver), runtimeExecutor); + }; + } else { + toolbox.synchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { + return std::make_unique(ownerBox, runtimeExecutor); + }; + + toolbox.asynchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { + return std::make_unique(ownerBox, runtimeExecutor); + }; + } RCTScheduler *scheduler = [[RCTScheduler alloc] initWithToolbox:toolbox]; scheduler.delegate = self; - auto reactNativeConfig = _contextContainer->at>("ReactNativeConfig"); - if (reactNativeConfig) { - _mountingManager.useModernDifferentiatorMode = - reactNativeConfig->getBool("react_fabric:enabled_optimized_moves_differ_ios"); - } - return scheduler; } diff --git a/React/Fabric/RCTSurfaceTouchHandler.mm b/React/Fabric/RCTSurfaceTouchHandler.mm index e2de89c668aa11..63c3699565abec 100644 --- a/React/Fabric/RCTSurfaceTouchHandler.mm +++ b/React/Fabric/RCTSurfaceTouchHandler.mm @@ -362,7 +362,7 @@ - (void)reset { [super reset]; - if (_activeTouches.size() != 0) { + if (!_activeTouches.empty()) { std::vector activeTouches; activeTouches.reserve(_activeTouches.size()); diff --git a/React/Fabric/Utils/PlatformRunLoopObserver.h b/React/Fabric/Utils/PlatformRunLoopObserver.h new file mode 100644 index 00000000000000..bbdf31fbe8a6be --- /dev/null +++ b/React/Fabric/Utils/PlatformRunLoopObserver.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 + +namespace facebook { +namespace react { + +/* + * Concrete iOS-specific implementation of `RunLoopObserver` using + * `CFRunLoopObserver` under the hood. + */ +class PlatformRunLoopObserver : public RunLoopObserver { + public: + PlatformRunLoopObserver( + RunLoopObserver::Activity activities, + RunLoopObserver::WeakOwner const &owner, + CFRunLoopRef runLoop); + + ~PlatformRunLoopObserver(); + + virtual bool isOnRunLoopThread() const noexcept override; + + private: + void startObserving() const noexcept override; + void stopObserving() const noexcept override; + + CFRunLoopRef runLoop_; + CFRunLoopObserverRef mainRunLoopObserver_; +}; + +/* + * Convenience specialization of `PlatformRunLoopObserver` observing the main + * run loop. + */ +class MainRunLoopObserver final : public PlatformRunLoopObserver { + public: + MainRunLoopObserver( + RunLoopObserver::Activity activities, + RunLoopObserver::WeakOwner const &owner) + : PlatformRunLoopObserver(activities, owner, CFRunLoopGetMain()) {} +}; + +} // namespace react +} // namespace facebook diff --git a/React/Fabric/Utils/PlatformRunLoopObserver.mm b/React/Fabric/Utils/PlatformRunLoopObserver.mm new file mode 100644 index 00000000000000..09731eda3b08b6 --- /dev/null +++ b/React/Fabric/Utils/PlatformRunLoopObserver.mm @@ -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. + */ + +#import "PlatformRunLoopObserver.h" + +#import + +namespace facebook { +namespace react { + +static CFRunLoopActivity toCFRunLoopActivity(RunLoopObserver::Activity activity) +{ + auto result = CFRunLoopActivity{}; + + if (RunLoopObserver::Activity(activity & RunLoopObserver::Activity::BeforeWaiting) == + RunLoopObserver::Activity::BeforeWaiting) { + result = result | kCFRunLoopBeforeWaiting; + } + + if (RunLoopObserver::Activity(activity & RunLoopObserver::Activity::AfterWaiting) == + RunLoopObserver::Activity::AfterWaiting) { + result = result | kCFRunLoopAfterWaiting; + } + + return result; +} + +static RunLoopObserver::Activity toRunLoopActivity(CFRunLoopActivity activity) +{ + auto result = RunLoopObserver::Activity{}; + + if (CFRunLoopActivity(activity & kCFRunLoopBeforeWaiting) == kCFRunLoopBeforeWaiting) { + result = RunLoopObserver::Activity(result | RunLoopObserver::Activity::BeforeWaiting); + } + + if (CFRunLoopActivity(activity & kCFRunLoopAfterWaiting) == kCFRunLoopAfterWaiting) { + result = RunLoopObserver::Activity(result | RunLoopObserver::Activity::AfterWaiting); + } + + return result; +} + +PlatformRunLoopObserver::PlatformRunLoopObserver( + RunLoopObserver::Activity activities, + RunLoopObserver::WeakOwner const &owner, + CFRunLoopRef runLoop) + : RunLoopObserver(activities, owner), runLoop_(runLoop) +{ + // A value (not a reference) to be captured by the block. + auto weakOwner = owner; + + // The documentation for `CFRunLoop` family API states that all of the methods are thread-safe. + // See "Thread Safety and Run Loop Objects" section of the "Threading Programming Guide" for more details. + mainRunLoopObserver_ = CFRunLoopObserverCreateWithHandler( + NULL /* allocator */, + toCFRunLoopActivity(activities_) /* activities */, + true /* repeats */, + 0 /* order */, + ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + auto strongOwner = weakOwner.lock(); + if (!strongOwner) { + return; + } + + this->activityDidChange(toRunLoopActivity(activity)); + }); + + assert(mainRunLoopObserver_); +} + +PlatformRunLoopObserver::~PlatformRunLoopObserver() +{ + stopObserving(); + CFRelease(mainRunLoopObserver_); +} + +void PlatformRunLoopObserver::startObserving() const noexcept +{ + CFRunLoopAddObserver(runLoop_, mainRunLoopObserver_, kCFRunLoopCommonModes); +} + +void PlatformRunLoopObserver::stopObserving() const noexcept +{ + CFRunLoopRemoveObserver(runLoop_, mainRunLoopObserver_, kCFRunLoopCommonModes); +} + +bool PlatformRunLoopObserver::isOnRunLoopThread() const noexcept +{ + return CFRunLoopGetCurrent() == runLoop_; +} + +} // namespace react +} // namespace facebook diff --git a/React/Views/RCTConvert+Transform.m b/React/Views/RCTConvert+Transform.m index 94e5dcd6cd91c4..06a0db3b4863e3 100644 --- a/React/Views/RCTConvert+Transform.m +++ b/React/Views/RCTConvert+Transform.m @@ -65,6 +65,7 @@ + (CATransform3D)CATransform3D:(id)json CGFloat zeroScaleThreshold = FLT_EPSILON; + CATransform3D next; for (NSDictionary *transformConfig in (NSArray *)json) { if (transformConfig.count != 1) { RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object."); @@ -74,10 +75,13 @@ + (CATransform3D)CATransform3D:(id)json id value = transformConfig[property]; if ([property isEqualToString:@"matrix"]) { - transform = [self CATransform3DFromMatrix:value]; + next = [self CATransform3DFromMatrix:value]; + transform = CATransform3DConcat(next, transform); } else if ([property isEqualToString:@"perspective"]) { - transform.m34 = -1 / [value floatValue]; + next = CATransform3DIdentity; + next.m34 = -1 / [value floatValue]; + transform = CATransform3DConcat(next, transform); } else if ([property isEqualToString:@"rotateX"]) { CGFloat rotate = [self convertToRadians:value]; @@ -123,11 +127,15 @@ + (CATransform3D)CATransform3D:(id)json } else if ([property isEqualToString:@"skewX"]) { CGFloat skew = [self convertToRadians:value]; - transform.m21 = tanf(skew); + next = CATransform3DIdentity; + next.m21 = tanf(skew); + transform = CATransform3DConcat(next, transform); } else if ([property isEqualToString:@"skewY"]) { CGFloat skew = [self convertToRadians:value]; - transform.m12 = tanf(skew); + next = CATransform3DIdentity; + next.m12 = tanf(skew); + transform = CATransform3DConcat(next, transform); } else { RCTLogError(@"Unsupported transform type for a CATransform3D: %@.", property); diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 595bfafb74804f..23d9fd5912bc9a 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -98,12 +98,12 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy task prepareHermes() { def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") if (!hermesPackagePath) { - throw new GradleScriptException("Could not find the hermes-engine npm package") + throw new GradleScriptException("Could not find the hermes-engine npm package", null) } def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar") if (!hermesAAR.exists()) { - throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"") + throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"", null) } def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" }) @@ -165,12 +165,12 @@ task prepareJSC { doLast { def jscPackagePath = findNodeModulePath(projectDir, "jsc-android") if (!jscPackagePath) { - throw new GradleScriptException("Could not find the jsc-android npm package") + throw new GradleScriptException("Could not find the jsc-android npm package", null) } def jscDist = file("$jscPackagePath/dist") if (!jscDist.exists()) { - throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory") + throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory", null) } def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile @@ -454,7 +454,7 @@ dependencies { extractJNI("com.facebook.fbjni:fbjni:0.0.2") testImplementation("junit:junit:${JUNIT_VERSION}") - testImplementation("org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}") + testImplementation("org.powermock:powermock-api-mockito2:${POWERMOCK_VERSION}") testImplementation("org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}") testImplementation("org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}") testImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}") diff --git a/ReactAndroid/gradle.properties b/ReactAndroid/gradle.properties index f7a5d3dc93b57c..1448ea45a04f9a 100644 --- a/ReactAndroid/gradle.properties +++ b/ReactAndroid/gradle.properties @@ -6,9 +6,9 @@ POM_NAME=ReactNative POM_ARTIFACT_ID=react-native POM_PACKAGING=aar -MOCKITO_CORE_VERSION=2.19.1 -POWERMOCK_VERSION=1.6.2 -ROBOLECTRIC_VERSION=3.0 +MOCKITO_CORE_VERSION=2.26.0 +POWERMOCK_VERSION=2.0.2 +ROBOLECTRIC_VERSION=4.3.1 JUNIT_VERSION=4.12 FEST_ASSERT_CORE_VERSION=2.0M10 diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK index ebfebaf7fe9f4e..980eda5d8d0601 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/network/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "network", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK index 798f91566e8184..0b29ef31268c22 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/BUCK @@ -28,7 +28,6 @@ rn_android_library( 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"), - react_native_target("java/com/facebook/react/modules/timepicker:timepicker"), react_native_target("java/com/facebook/react/touch:touch"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TimePickerDialogTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TimePickerDialogTestCase.java deleted file mode 100644 index 4325a3a6af4c46..00000000000000 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TimePickerDialogTestCase.java +++ /dev/null @@ -1,156 +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.TimePickerDialog; -import android.content.DialogInterface; -import androidx.fragment.app.DialogFragment; -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.timepicker.TimePickerDialogModule; -import com.facebook.react.testing.ReactAppInstrumentationTestCase; -import com.facebook.react.testing.ReactInstanceSpecForTest; -import java.util.ArrayList; -import java.util.List; - -/** Test case for {@link TimePickerDialogModule} options and callbacks. */ -public class TimePickerDialogTestCase extends ReactAppInstrumentationTestCase { - - private static interface TimePickerDialogTestModule extends JavaScriptModule { - public void showTimePickerDialog(WritableMap options); - } - - private static class TimePickerDialogRecordingModule extends BaseJavaModule { - - private final List mTimes = new ArrayList(); - private int mDismissed = 0; - private int mErrors = 0; - - @Override - public String getName() { - return "TimePickerDialogRecordingModule"; - } - - @ReactMethod - public void recordTime(int hour, int minute) { - mTimes.add(new Integer[] {hour, minute}); - } - - @ReactMethod - public void recordDismissed() { - mDismissed++; - } - - @ReactMethod - public void recordError() { - mErrors++; - } - - public List getTimes() { - return new ArrayList(mTimes); - } - - public int getDismissed() { - return mDismissed; - } - - public int getErrors() { - return mErrors; - } - } - - final TimePickerDialogRecordingModule mRecordingModule = new TimePickerDialogRecordingModule(); - - @Override - protected ReactInstanceSpecForTest createReactInstanceSpecForTest() { - return super.createReactInstanceSpecForTest().addNativeModule(mRecordingModule); - } - - @Override - protected String getReactApplicationKeyUnderTest() { - return "TimePickerDialogTestApp"; - } - - private TimePickerDialogTestModule getTestModule() { - return getReactContext().getCatalystInstance().getJSModule(TimePickerDialogTestModule.class); - } - - private DialogFragment showDialog(WritableMap options) { - getTestModule().showTimePickerDialog(options); - - waitForBridgeAndUIIdle(); - getInstrumentation().waitForIdleSync(); - - return (DialogFragment) - getActivity() - .getSupportFragmentManager() - .findFragmentByTag(TimePickerDialogModule.FRAGMENT_TAG); - } - - public void testShowBasicTimePicker() { - final DialogFragment fragment = showDialog(null); - - assertNotNull(fragment); - } - - public void testPresetTimeAndCallback() throws Throwable { - final WritableMap options = new WritableNativeMap(); - options.putInt("hour", 4); - options.putInt("minute", 5); - - final DialogFragment fragment = showDialog(options); - - List recordedTimes = mRecordingModule.getTimes(); - assertEquals(0, recordedTimes.size()); - - runTestOnUiThread( - new Runnable() { - @Override - public void run() { - ((TimePickerDialog) fragment.getDialog()) - .getButton(DialogInterface.BUTTON_POSITIVE) - .performClick(); - } - }); - - getInstrumentation().waitForIdleSync(); - waitForBridgeAndUIIdle(); - - assertEquals(0, mRecordingModule.getErrors()); - assertEquals(0, mRecordingModule.getDismissed()); - - recordedTimes = mRecordingModule.getTimes(); - assertEquals(1, recordedTimes.size()); - assertEquals(4, (int) recordedTimes.get(0)[0]); - assertEquals(5, (int) recordedTimes.get(0)[1]); - } - - public void testDismissCallback() throws Throwable { - final DialogFragment fragment = showDialog(null); - - assertEquals(0, mRecordingModule.getDismissed()); - - runTestOnUiThread( - new Runnable() { - @Override - public void run() { - fragment.getDialog().dismiss(); - } - }); - - getInstrumentation().waitForIdleSync(); - waitForBridgeAndUIIdle(); - - assertEquals(0, mRecordingModule.getErrors()); - assertEquals(0, mRecordingModule.getTimes().size()); - assertEquals(1, mRecordingModule.getDismissed()); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK b/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK index c1ee4ded0329dd..2daef176010bf5 100644 --- a/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/debug/holder/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "holder", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK b/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK index 876be9f2ba6753..3e2e015c78e8d2 100644 --- a/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/debug/tags/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "tags", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK index cec027de58b101..94067e4f20f33d 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "IS_OSS_BU rn_android_library( name = "FBReactNativeSpec", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = ["PUBLIC"], deps = [ react_native_dep("third-party/java/jsr-305:jsr-305"), @@ -34,7 +34,7 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID,), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeTimePickerAndroidSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeTimePickerAndroidSpec.java deleted file mode 100644 index e9707f4f3b3c86..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeTimePickerAndroidSpec.java +++ /dev/null @@ -1,30 +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 an internal genrule from Flow types. - * - * @generated - * @nolint - */ - -package com.facebook.fbreact.specs; - -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReactModuleWithSpec; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.turbomodule.core.interfaces.TurboModule; - -public abstract class NativeTimePickerAndroidSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { - public NativeTimePickerAndroidSpec(ReactApplicationContext reactContext) { - super(reactContext); - } - - @ReactMethod - public abstract void open(ReadableMap options, Promise promise); -} diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeUIManagerSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeUIManagerSpec.java index 1f9cdce0522832..03e3fe9bb0ce48 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeUIManagerSpec.java +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeUIManagerSpec.java @@ -42,9 +42,6 @@ public NativeUIManagerSpec(ReactApplicationContext reactContext) { public abstract void configureNextLayoutAnimation(ReadableMap config, Callback callback, Callback errorCallback); - @ReactMethod - public abstract void playTouchSound(); - @ReactMethod public abstract void blur(Double reactTag); diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp index 069f845b9fde02..62189bc3aba3e3 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp @@ -2059,26 +2059,6 @@ namespace facebook { - } - - } // namespace react -} // namespace facebook -namespace facebook { - namespace react { - - - static facebook::jsi::Value __hostFunction_NativeTimePickerAndroidSpecJSI_open(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeJavaMethod(rt, PromiseKind, "open", "(Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Promise;)V", args, count); - } - - - NativeTimePickerAndroidSpecJSI::NativeTimePickerAndroidSpecJSI(const JavaTurboModule::InitParams ¶ms) - : JavaTurboModule(params) { - - methodMap_["open"] = MethodMetadata {1, __hostFunction_NativeTimePickerAndroidSpecJSI_open}; - - - } } // namespace react @@ -2170,10 +2150,6 @@ namespace facebook { return static_cast(turboModule).invokeJavaMethod(rt, ArrayKind, "getDefaultEventTypes", "()Lcom/facebook/react/bridge/WritableArray;", args, count); } - static facebook::jsi::Value __hostFunction_NativeUIManagerSpecJSI_playTouchSound(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeJavaMethod(rt, VoidKind, "playTouchSound", "()V", args, count); - } - static facebook::jsi::Value __hostFunction_NativeUIManagerSpecJSI_lazilyLoadView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeJavaMethod(rt, ObjectKind, "lazilyLoadView", "(Ljava/lang/String;)Lcom/facebook/react/bridge/WritableMap;", args, count); } @@ -2280,9 +2256,6 @@ namespace facebook { methodMap_["getDefaultEventTypes"] = MethodMetadata {0, __hostFunction_NativeUIManagerSpecJSI_getDefaultEventTypes}; - methodMap_["playTouchSound"] = MethodMetadata {0, __hostFunction_NativeUIManagerSpecJSI_playTouchSound}; - - methodMap_["lazilyLoadView"] = MethodMetadata {1, __hostFunction_NativeUIManagerSpecJSI_lazilyLoadView}; diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h index eb22a864c9bfa7..bb12df936070cd 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h @@ -731,20 +731,6 @@ namespace facebook { } // namespace react } // namespace facebook -namespace facebook { - namespace react { - /** - * C++ class for module 'TimePickerAndroid' - */ - - class JSI_EXPORT NativeTimePickerAndroidSpecJSI : public JavaTurboModule { - public: - NativeTimePickerAndroidSpecJSI(const JavaTurboModule::InitParams ¶ms); - - }; - } // namespace react -} // namespace facebook - namespace facebook { namespace react { /** diff --git a/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK b/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK index 3b4ea04dbfdff0..439ae4b4743578 100644 --- a/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/hermes/instrumentation/BUCK @@ -11,7 +11,7 @@ rn_android_library( rn_android_library( name = "hermes_samplingprofiler", srcs = ["HermesSamplingProfiler.java"], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = ["PUBLIC"], deps = [ react_native_dep("java/com/facebook/proguard/annotations:annotations"), @@ -30,7 +30,7 @@ rn_xplat_cxx_library( headers = ["HermesSamplingProfiler.h"], header_namespace = "", compiler_flags = ["-fexceptions"], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, soname = "libjsijniprofiler.$(ext)", visibility = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/BUCK b/ReactAndroid/src/main/java/com/facebook/react/BUCK index c8728a8d441754..f18cc78f8f02c7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "react", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 59ccbbedefb4b8..63ac2ec11dfb62 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -1152,18 +1152,30 @@ private void attachRootViewToInstance(final ReactRoot reactRoot) { final int rootTag; - if (reactRoot.getUIManagerType() == FABRIC) { - rootTag = - uiManager.startSurface( - reactRoot.getRootViewGroup(), - reactRoot.getJSModuleName(), - initialProperties == null - ? new WritableNativeMap() - : Arguments.fromBundle(initialProperties), - reactRoot.getWidthMeasureSpec(), - reactRoot.getHeightMeasureSpec()); - reactRoot.setRootViewTag(rootTag); - reactRoot.setShouldLogContentAppeared(true); + if (ReactFeatureFlags.enableFabricStartSurfaceWithLayoutMetrics) { + if (reactRoot.getUIManagerType() == FABRIC) { + rootTag = + uiManager.startSurface( + reactRoot.getRootViewGroup(), + reactRoot.getJSModuleName(), + initialProperties == null + ? new WritableNativeMap() + : Arguments.fromBundle(initialProperties), + reactRoot.getWidthMeasureSpec(), + reactRoot.getHeightMeasureSpec()); + reactRoot.setRootViewTag(rootTag); + reactRoot.setShouldLogContentAppeared(true); + } else { + rootTag = + uiManager.addRootView( + reactRoot.getRootViewGroup(), + initialProperties == null + ? new WritableNativeMap() + : Arguments.fromBundle(initialProperties), + reactRoot.getInitialUITemplate()); + reactRoot.setRootViewTag(rootTag); + reactRoot.runApplication(); + } } else { rootTag = uiManager.addRootView( @@ -1173,8 +1185,17 @@ private void attachRootViewToInstance(final ReactRoot reactRoot) { : Arguments.fromBundle(initialProperties), reactRoot.getInitialUITemplate()); reactRoot.setRootViewTag(rootTag); - reactRoot.runApplication(); + if (reactRoot.getUIManagerType() == FABRIC) { + // Fabric requires to call updateRootLayoutSpecs before starting JS Application, + // this ensures the root will hace the correct pointScaleFactor. + uiManager.updateRootLayoutSpecs( + rootTag, reactRoot.getWidthMeasureSpec(), reactRoot.getHeightMeasureSpec()); + reactRoot.setShouldLogContentAppeared(true); + } else { + reactRoot.runApplication(); + } } + Systrace.beginAsyncSection( TRACE_TAG_REACT_JAVA_BRIDGE, "pre_rootView.onAttachedToReactInstance", rootTag); UiThreadUtil.runOnUiThread( @@ -1303,6 +1324,9 @@ private ReactApplicationContext createReactContext( } } } + if (ReactFeatureFlags.eagerInitializeFabric) { + catalystInstance.getJSIModule(JSIModuleType.UIManager); + } if (mBridgeIdleDebugListener != null) { catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK b/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK index 57e9752b640e08..c3f9021b086d2e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/BUCK @@ -6,7 +6,7 @@ rn_android_library( "*.java", ]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index ead07f9512229b..e67723c7b13f76 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -7,7 +7,9 @@ package com.facebook.react.animated; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; import com.facebook.common.logging.FLog; import com.facebook.fbreact.specs.NativeAnimatedModuleSpec; import com.facebook.infer.annotation.Assertions; @@ -16,6 +18,8 @@ import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.UIManager; +import com.facebook.react.bridge.UIManagerListener; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; @@ -26,8 +30,9 @@ import com.facebook.react.uimanager.NativeViewHierarchyManager; import com.facebook.react.uimanager.UIBlock; import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.UIManagerModuleListener; import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * Module that exposes interface for creating and managing animated nodes on the "native" side. @@ -75,7 +80,7 @@ */ @ReactModule(name = NativeAnimatedModule.NAME) public class NativeAnimatedModule extends NativeAnimatedModuleSpec - implements LifecycleEventListener, UIManagerModuleListener { + implements LifecycleEventListener, UIManagerListener { public static final String NAME = "NativeAnimatedModule"; @@ -83,10 +88,13 @@ private interface UIThreadOperation { void execute(NativeAnimatedNodesManager animatedNodesManager); } - private final GuardedFrameCallback mAnimatedFrameCallback; + @NonNull private final GuardedFrameCallback mAnimatedFrameCallback; private final ReactChoreographer mReactChoreographer; - private ArrayList mOperations = new ArrayList<>(); - private ArrayList mPreOperations = new ArrayList<>(); + @NonNull private List mOperations = new ArrayList<>(); + @NonNull private List mPreOperations = new ArrayList<>(); + + @NonNull private List mPreOperationsUIBlock = new ArrayList<>(); + @NonNull private List mOperationsUIBlock = new ArrayList<>(); private @Nullable NativeAnimatedNodesManager mNodesManager; @@ -132,7 +140,7 @@ public void initialize() { reactApplicationContext.addLifecycleEventListener(this); UIManagerModule uiManager = Assertions.assertNotNull(reactApplicationContext.getNativeModule(UIManagerModule.class)); - uiManager.addUIManagerListener(this); + uiManager.addUIManagerEventListener(this); } } @@ -141,35 +149,94 @@ public void onHostResume() { enqueueFrameCallback(); } + // For FabricUIManager @Override - public void willDispatchViewUpdates(final UIManagerModule uiManager) { + @UiThread + public void willDispatchPreMountItems() { + if (mPreOperationsUIBlock.size() != 0) { + List preOperations = mPreOperationsUIBlock; + mPreOperationsUIBlock = new ArrayList<>(); + + for (UIBlock op : preOperations) { + op.execute(null); + } + } + } + + // For FabricUIManager + @Override + @UiThread + public void willDispatchMountItems() { + if (mOperationsUIBlock.size() != 0) { + List operations = mOperationsUIBlock; + mOperationsUIBlock = new ArrayList<>(); + + for (UIBlock op : operations) { + op.execute(null); + } + } + } + + // For non-FabricUIManager + @Override + @UiThread + public void willDispatchViewUpdates(final UIManager uiManager) { if (mOperations.isEmpty() && mPreOperations.isEmpty()) { return; } - final ArrayList preOperations = mPreOperations; - final ArrayList operations = mOperations; + + final AtomicBoolean hasRunPreOperations = new AtomicBoolean(false); + final AtomicBoolean hasRunOperations = new AtomicBoolean(false); + final List preOperations = mPreOperations; + final List operations = mOperations; mPreOperations = new ArrayList<>(); mOperations = new ArrayList<>(); - uiManager.prependUIBlock( + + // This is kind of a hack. Basically UIManagerListener cannot import UIManagerModule + // (that would cause an import cycle) and they're not in the same package. But, + // UIManagerModule is the only thing that calls `willDispatchViewUpdates` so we + // know this is safe. + // This goes away entirely in Fabric/Venice. + UIManagerModule uiManagerModule = (UIManagerModule) uiManager; + + UIBlock preOperationsUIBlock = new UIBlock() { @Override public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { + if (!hasRunPreOperations.compareAndSet(false, true)) { + return; + } + NativeAnimatedNodesManager nodesManager = getNodesManager(); for (UIThreadOperation operation : preOperations) { operation.execute(nodesManager); } } - }); - uiManager.addUIBlock( + }; + + UIBlock operationsUIBlock = new UIBlock() { @Override public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { + if (!hasRunOperations.compareAndSet(false, true)) { + return; + } + NativeAnimatedNodesManager nodesManager = getNodesManager(); for (UIThreadOperation operation : operations) { operation.execute(nodesManager); } } - }); + }; + + // Queue up operations for Fabric + mPreOperationsUIBlock.add(preOperationsUIBlock); + mOperationsUIBlock.add(operationsUIBlock); + + // Here we queue up the UI Blocks for the old, non-Fabric UIManager. + // We queue them in both systems, let them race, and see which wins. + uiManagerModule.prependUIBlock(preOperationsUIBlock); + uiManagerModule.addUIBlock(operationsUIBlock); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK b/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK index 53ac6202190a9c..fcf60e30696589 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/BUCK @@ -21,7 +21,7 @@ rn_android_library( exclude = INTERFACES, ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], proguard_config = "reactnative.pro", provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), @@ -66,7 +66,7 @@ rn_android_library( name = "interfaces", srcs = glob(INTERFACES), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], proguard_config = "reactnative.pro", provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java index 170fc7fcd3f25e..4edebdcf00ea54 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java @@ -91,4 +91,19 @@ int startSurface( * @param eventType */ void sendAccessibilityEvent(int reactTag, int eventType); + + /** + * Register a {@link UIManagerListener} with this UIManager to receive lifecycle callbacks. + * + * @param listener + */ + void addUIManagerEventListener(UIManagerListener listener); + + /** + * Unregister a {@link UIManagerListener} from this UIManager to stop receiving lifecycle + * callbacks. + * + * @param listener + */ + void removeUIManagerEventListener(UIManagerListener listener); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java new file mode 100644 index 00000000000000..216d38b00d6495 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.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; + +/** Listener used to hook into the UIManager update process. */ +public interface UIManagerListener { + /** + * Called right before view updates are dispatched at the end of a batch. This is useful if a + * module needs to add UIBlocks to the queue before it is flushed. + */ + void willDispatchViewUpdates(UIManager uiManager); + /** Called on the UI thread right before normal mount items are executed. */ + void willDispatchMountItems(); + /** Called on the UI thread right before premount items are executed. */ + void willDispatchPreMountItems(); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/BUCK b/ReactAndroid/src/main/java/com/facebook/react/common/BUCK index 0e5a4b52caeac1..ef52fb6fb78abf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/common/BUCK @@ -11,7 +11,7 @@ rn_android_library( exclude = SUB_PROJECTS, ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK b/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK index 86005fbc76ae79..25f39b59688c28 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/common/network/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "network", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/BUCK b/ReactAndroid/src/main/java/com/facebook/react/config/BUCK index 347452197e226b..6120fdec936c2d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/config/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library") rn_android_library( name = "config", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index c04a883d5fb5cc..c06d0e0c3508d0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -38,6 +38,13 @@ public class ReactFeatureFlags { */ public static boolean useViewManagerDelegates = 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. + */ + public static boolean useViewManagerDelegatesForCommands = false; + /** * Should this application use Catalyst Teardown V2? This is an experiment to use a V2 of the * CatalystInstanceImpl `destroy` method. @@ -76,4 +83,10 @@ public class ReactFeatureFlags { * remove this when bug is fixed */ public static boolean enableTransitionLayoutOnlyViewCleanup = false; + + /** Feature flag to configure eager initialization of Fabric */ + public static boolean eagerInitializeFabric = false; + + /** Feature flag to configure initialization of Fabric surfaces. */ + public static boolean enableFabricStartSurfaceWithLayoutMetrics = true; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK index 1c2b6c72e761b4..7680650ab6a62f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "devsupport", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], manifest = "AndroidManifest.xml", provided_deps = [ react_native_dep("third-party/android/androidx:core"), @@ -47,7 +47,7 @@ rn_android_library( name = "interfaces", srcs = glob(["interfaces/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index 5c5bad9eb4a3a2..b539282d635215 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -421,12 +421,13 @@ private boolean getJSMinifyMode() { private String createBundleURL(String mainModuleID, BundleType type, String host) { return String.format( Locale.US, - "http://%s/%s.%s?platform=android&dev=%s&minify=%s", + "http://%s/%s.%s?platform=android&dev=%s&minify=%s&app=%s", host, mainModuleID, type.typeID(), getDevMode(), - getJSMinifyMode()); + getJSMinifyMode(), + mPackageName); } private String createBundleURL(String mainModuleID, BundleType type) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK index 4aa1d97440a5f3..9ebcd431161f44 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK @@ -9,7 +9,7 @@ rn_android_library( "mounting/**/*.java", ]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java index 81ab4e76829cff..d0abc11e6dc651 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java @@ -70,6 +70,8 @@ public native void setConstraints( boolean isRTL, boolean doLeftAndRightSwapInRTL); + public native void driveCxxAnimations(); + public void register( @NonNull JavaScriptContextHolder jsContext, @NonNull FabricUIManager fabricUIManager, diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java index 0caf99934dd9f2..62e020c7118bdd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java @@ -33,7 +33,6 @@ 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.UpdateLocalDataMountItem; import com.facebook.react.fabric.mounting.mountitems.UpdatePaddingMountItem; import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem; import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem; @@ -123,7 +122,6 @@ private static void loadClasses() { SendAccessibilityEvent.class.getClass(); UpdateEventEmitterMountItem.class.getClass(); UpdateLayoutMountItem.class.getClass(); - UpdateLocalDataMountItem.class.getClass(); UpdatePaddingMountItem.class.getClass(); UpdatePropsMountItem.class.getClass(); UpdateStateMountItem.class.getClass(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index e5239c8db9835d..b80ee9f25c563c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -42,6 +42,7 @@ 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.config.ReactFeatureFlags; @@ -63,7 +64,6 @@ 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.UpdateLocalDataMountItem; import com.facebook.react.fabric.mounting.mountitems.UpdatePaddingMountItem; import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem; import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem; @@ -83,6 +83,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; @SuppressLint("MissingNativeLoadLibrary") public class FabricUIManager implements UIManager, LifecycleEventListener { @@ -124,6 +125,9 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { @NonNull private List mViewCommandMountItems = new ArrayList<>(); + @NonNull + private final CopyOnWriteArrayList mListeners = new CopyOnWriteArrayList<>(); + @GuardedBy("mMountItemsLock") @NonNull private List mMountItems = new ArrayList<>(); @@ -143,6 +147,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { */ private volatile boolean mDestroyed = false; + private boolean mDriveCxxAnimations = false; + private long mRunStartTime = 0l; private long mBatchedExecutionTime = 0l; private long mDispatchViewUpdatesTime = 0l; @@ -395,14 +401,6 @@ private MountItem updatePropsMountItem(int reactTag, ReadableMap map) { return new UpdatePropsMountItem(reactTag, map); } - @DoNotStrip - @SuppressWarnings("unused") - @AnyThread - @ThreadConfined(ANY) - private MountItem updateLocalDataMountItem(int reactTag, ReadableMap newLocalData) { - return new UpdateLocalDataMountItem(reactTag, newLocalData); - } - @DoNotStrip @SuppressWarnings("unused") @AnyThread @@ -464,7 +462,7 @@ private long measure( float maxWidth, float minHeight, float maxHeight, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { ReactContext context = rootTag < 0 ? mReactApplicationContext : mReactContextForRootTag.get(rootTag); return mMountingManager.measure( @@ -507,6 +505,14 @@ public void synchronouslyUpdateViewOnUIThread(int reactTag, @NonNull ReadableMap } } + public void addUIManagerEventListener(UIManagerListener listener) { + mListeners.add(listener); + } + + public void removeUIManagerEventListener(UIManagerListener listener) { + mListeners.remove(listener); + } + /** * This method enqueues UI operations directly to the UI thread. This might change in the future * to enforce execution order using {@link ReactChoreographer#CallbackType}. @@ -588,6 +594,10 @@ private void tryDispatchMountItems() { return; } + for (UIManagerListener listener : mListeners) { + listener.willDispatchMountItems(); + } + final boolean didDispatchItems; try { didDispatchItems = dispatchMountItems(); @@ -773,12 +783,16 @@ private boolean dispatchMountItems() { // 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 retryable mounting layer instruction: " - + mountItem.toString(), - e)); + if (mountItem instanceof DispatchCommandMountItem) { + ReactSoftException.logSoftException( + TAG, + new ReactNoCrashSoftException( + "Caught exception executing retryable mounting layer instruction: " + + mountItem.toString(), + e)); + } else { + throw e; + } } } mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime; @@ -797,6 +811,10 @@ private void dispatchPreMountItems(long frameTimeNanos) { // reentering during dispatchPreMountItems mInDispatch = true; + for (UIManagerListener listener : mListeners) { + listener.willDispatchPreMountItems(); + } + try { while (true) { long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000); @@ -972,6 +990,20 @@ public void profileNextBatch() { // TODO T31905686: Remove this method and add support for multi-threading performance counters } + // Called from Binding.cpp + @DoNotStrip + @AnyThread + public void onAnimationStarted() { + mDriveCxxAnimations = true; + } + + // Called from Binding.cpp + @DoNotStrip + @AnyThread + public void onAllAnimationsComplete() { + mDriveCxxAnimations = false; + } + @Override public Map getPerformanceCounters() { HashMap performanceCounters = new HashMap<>(); @@ -1007,11 +1039,18 @@ public void doFrameGuarded(long frameTimeNanos) { return; } + // Drive any animations from C++. + // There is a race condition here between getting/setting + // `mDriveCxxAnimations` which shouldn't matter; it's safe to call + // the mBinding method, unless mBinding has gone away. + if (mDriveCxxAnimations && mBinding != null) { + mBinding.driveCxxAnimations(); + } + try { dispatchPreMountItems(frameTimeNanos); tryDispatchMountItems(); - } catch (Exception ex) { FLog.e(TAG, "Exception thrown when executing UIFrameGuarded", ex); stop(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK index 83d5ba3a4fb4fc..7fc222b4702f8c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/BUCK @@ -18,7 +18,7 @@ rn_xplat_cxx_library( "-frtti", ], fbandroid_allow_jni_merging = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", @@ -29,6 +29,7 @@ rn_xplat_cxx_library( deps = [ react_native_xplat_target("better:better"), react_native_xplat_target("config:config"), + react_native_xplat_target("fabric/animations:animations"), react_native_xplat_target("fabric/uimanager:uimanager"), react_native_xplat_target("fabric/scheduler:scheduler"), react_native_xplat_target("fabric/componentregistry:componentregistry"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index e188e9d336e688..025d035502e46f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,10 @@ std::shared_ptr Binding::getScheduler() { return scheduler_; } +LayoutAnimationDriver *Binding::getAnimationDriver() { + return (animationDriver_ ? animationDriver_.get() : nullptr); +} + void Binding::startSurface( jint surfaceId, jni::alias_ref moduleName, @@ -91,7 +96,8 @@ void Binding::startSurface( moduleName->toStdString(), initialProps->consume(), {}, - context); + context, + getAnimationDriver()); } void Binding::startSurfaceWithConstraints( @@ -106,8 +112,11 @@ void Binding::startSurfaceWithConstraints( jboolean doLeftAndRightSwapInRTL) { SystraceSection s("FabricUIManagerBinding::startSurfaceWithConstraints"); - LOG(WARNING) << "Binding::startSurfaceWithConstraints() was called (address: " - << this << ", surfaceId: " << surfaceId << ")."; + if (enableFabricLogs_) { + LOG(WARNING) + << "Binding::startSurfaceWithConstraints() was called (address: " + << this << ", surfaceId: " << surfaceId << ")."; + } std::shared_ptr scheduler = getScheduler(); if (!scheduler) { @@ -134,7 +143,8 @@ void Binding::startSurfaceWithConstraints( moduleName->toStdString(), initialProps->consume(), constraints, - context); + context, + getAnimationDriver()); } void Binding::renderTemplateToSurface(jint surfaceId, jstring uiTemplate) { @@ -155,8 +165,10 @@ void Binding::renderTemplateToSurface(jint surfaceId, jstring uiTemplate) { void Binding::stopSurface(jint surfaceId) { SystraceSection s("FabricUIManagerBinding::stopSurface"); - LOG(WARNING) << "Binding::stopSurface() was called (address: " << this - << ", surfaceId: " << surfaceId << ")."; + if (enableFabricLogs_) { + LOG(WARNING) << "Binding::stopSurface() was called (address: " << this + << ", surfaceId: " << surfaceId << ")."; + } std::shared_ptr scheduler = getScheduler(); if (!scheduler) { @@ -209,8 +221,16 @@ void Binding::installFabricUIManager( jni::alias_ref reactNativeConfig) { SystraceSection s("FabricUIManagerBinding::installFabricUIManager"); - LOG(WARNING) << "Binding::installFabricUIManager() was called (address: " - << this << ")."; + std::shared_ptr config = + std::make_shared(reactNativeConfig); + + enableFabricLogs_ = + config->getBool("react_fabric:enabled_android_fabric_logs"); + + if (enableFabricLogs_) { + LOG(WARNING) << "Binding::installFabricUIManager() was called (address: " + << this << ")."; + } // Use std::lock and std::adopt_lock to prevent deadlocks by locking mutexes // at the same time @@ -253,8 +273,6 @@ void Binding::installFabricUIManager( ownerBox, eventBeatManager, runtimeExecutor, localJavaUIManager); }; - std::shared_ptr config = - std::make_shared(reactNativeConfig); contextContainer->insert("ReactNativeConfig", config); contextContainer->insert("FabricUIManager", javaUIManager_); @@ -265,14 +283,11 @@ void Binding::installFabricUIManager( collapseDeleteCreateMountingInstructions_ = reactNativeConfig_->getBool( "react_fabric:enabled_collapse_delete_create_mounting_instructions"); - disableVirtualNodePreallocation_ = reactNativeConfig_->getBool( - "react_fabric:disable_virtual_node_preallocation"); - disablePreallocateViews_ = reactNativeConfig_->getBool( "react_fabric:disabled_view_preallocation_android"); - enableOptimizedMovesDiffer_ = reactNativeConfig_->getBool( - "react_fabric:enabled_optimized_moves_differ_android"); + bool enableLayoutAnimations_ = reactNativeConfig_->getBool( + "react_fabric:enabled_layout_animations_android"); auto toolbox = SchedulerToolbox{}; toolbox.contextContainer = contextContainer; @@ -280,12 +295,18 @@ void Binding::installFabricUIManager( toolbox.runtimeExecutor = runtimeExecutor; toolbox.synchronousEventBeatFactory = synchronousBeatFactory; toolbox.asynchronousEventBeatFactory = asynchronousBeatFactory; - scheduler_ = std::make_shared(toolbox, this); + + if (enableLayoutAnimations_) { + animationDriver_ = std::make_unique(this); + } + scheduler_ = std::make_shared(toolbox, getAnimationDriver(), this); } void Binding::uninstallFabricUIManager() { - LOG(WARNING) << "Binding::uninstallFabricUIManager() was called (address: " - << this << ")."; + if (enableFabricLogs_) { + LOG(WARNING) << "Binding::uninstallFabricUIManager() was called (address: " + << this << ")."; + } // Use std::lock and std::adopt_lock to prevent deadlocks by locking mutexes // at the same time std::lock(schedulerMutex_, javaUIManagerMutex_); @@ -583,9 +604,7 @@ void Binding::schedulerDidFinishTransaction( return; } - auto mountingTransaction = mountingCoordinator->pullTransaction( - enableOptimizedMovesDiffer_ ? DifferentiatorMode::OptimizedMoves - : DifferentiatorMode::Classic); + auto mountingTransaction = mountingCoordinator->pullTransaction(); if (!mountingTransaction.has_value()) { return; @@ -862,6 +881,37 @@ void Binding::setPixelDensity(float pointScaleFactor) { pointScaleFactor_ = pointScaleFactor; } +void Binding::onAnimationStarted() { + jni::global_ref localJavaUIManager = getJavaUIManager(); + if (!localJavaUIManager) { + LOG(ERROR) << "Binding::animationsStarted: JavaUIManager disappeared"; + return; + } + + static auto layoutAnimationsStartedJNI = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("onAnimationStarted"); + + layoutAnimationsStartedJNI(localJavaUIManager); +} +void Binding::onAllAnimationsComplete() { + jni::global_ref localJavaUIManager = getJavaUIManager(); + if (!localJavaUIManager) { + LOG(ERROR) << "Binding::allAnimationsComplete: JavaUIManager disappeared"; + return; + } + + static auto allAnimationsCompleteJNI = + jni::findClassStatic(UIManagerJavaDescriptor) + ->getMethod("onAllAnimationsComplete"); + + allAnimationsCompleteJNI(localJavaUIManager); +} + +void Binding::driveCxxAnimations() { + scheduler_->animationTick(); +} + void Binding::schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, const ShadowView &shadowView) { @@ -986,6 +1036,7 @@ void Binding::registerNatives() { makeNativeMethod("stopSurface", Binding::stopSurface), makeNativeMethod("setConstraints", Binding::setConstraints), makeNativeMethod("setPixelDensity", Binding::setPixelDensity), + makeNativeMethod("driveCxxAnimations", Binding::driveCxxAnimations), makeNativeMethod( "uninstallFabricUIManager", Binding::uninstallFabricUIManager)}); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h index 2d7128d23875d1..504fc83b252074 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h @@ -8,10 +8,12 @@ #pragma once #include +#include #include #include #include #include +#include #include #include #include "ComponentFactoryDelegate.h" @@ -22,7 +24,9 @@ namespace react { class Instance; -class Binding : public jni::HybridClass, public SchedulerDelegate { +class Binding : public jni::HybridClass, + public SchedulerDelegate, + public LayoutAnimationStatusDelegate { public: constexpr static const char *const kJavaDescriptor = "Lcom/facebook/react/fabric/Binding;"; @@ -73,26 +77,28 @@ class Binding : public jni::HybridClass, public SchedulerDelegate { void stopSurface(jint surfaceId); void schedulerDidFinishTransaction( - MountingCoordinator::Shared const &mountingCoordinator); + MountingCoordinator::Shared const &mountingCoordinator) override; void schedulerDidRequestPreliminaryViewAllocation( const SurfaceId surfaceId, - const ShadowView &shadowView); + const ShadowView &shadowView) override; void schedulerDidDispatchCommand( const ShadowView &shadowView, std::string const &commandName, - folly::dynamic const args); - - void setPixelDensity(float pointScaleFactor); + folly::dynamic const args) override; void schedulerDidSetJSResponder( SurfaceId surfaceId, const ShadowView &shadowView, const ShadowView &initialShadowView, - bool blockNativeResponder); + bool blockNativeResponder) override; + + void schedulerDidClearJSResponder() override; - void schedulerDidClearJSResponder(); + void setPixelDensity(float pointScaleFactor); + + void driveCxxAnimations(); void uninstallFabricUIManager(); @@ -100,6 +106,12 @@ class Binding : public jni::HybridClass, public SchedulerDelegate { jni::global_ref javaUIManager_; std::mutex javaUIManagerMutex_; + // LayoutAnimations + virtual void onAnimationStarted() override; + virtual void onAllAnimationsComplete() override; + LayoutAnimationDriver *getAnimationDriver(); + std::unique_ptr animationDriver_; + std::shared_ptr scheduler_; std::mutex schedulerMutex_; @@ -112,7 +124,7 @@ class Binding : public jni::HybridClass, public SchedulerDelegate { bool collapseDeleteCreateMountingInstructions_{false}; bool disablePreallocateViews_{false}; bool disableVirtualNodePreallocation_{false}; - bool enableOptimizedMovesDiffer_{false}; + bool enableFabricLogs_{false}; }; } // namespace react diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 79966769fd79d7..e48192e8963119 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -138,7 +138,7 @@ public void addViewAt(int parentTag, int tag, int index) { private @NonNull ViewState getViewState(int tag) { ViewState viewState = mTagToViewState.get(tag); if (viewState == null) { - throw new IllegalStateException("Unable to find viewState view for tag " + tag); + throw new RetryableMountingLayerException("Unable to find viewState view for tag " + tag); } return viewState; } @@ -202,11 +202,13 @@ public void sendAccessibilityEvent(int reactTag, int eventType) { ViewState viewState = getViewState(reactTag); if (viewState.mViewManager == null) { - throw new IllegalStateException("Unable to find viewState manager for tag " + reactTag); + throw new RetryableMountingLayerException( + "Unable to find viewState manager for tag " + reactTag); } if (viewState.mView == null) { - throw new IllegalStateException("Unable to find viewState view for tag " + reactTag); + throw new RetryableMountingLayerException( + "Unable to find viewState view for tag " + reactTag); } viewState.mView.sendAccessibilityEvent(eventType); @@ -240,7 +242,38 @@ public void removeViewAt(int parentTag, int index) { throw new IllegalStateException("Unable to find view for tag " + parentTag); } - getViewGroupManager(viewState).removeViewAt(parentView, index); + ViewGroupManager viewGroupManager = getViewGroupManager(viewState); + + 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); + + 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); + } } @UiThread @@ -371,37 +404,6 @@ public void deleteView(int reactTag) { } } - @UiThread - public void updateLocalData(int reactTag, @NonNull ReadableMap newLocalData) { - UiThreadUtil.assertOnUiThread(); - ViewState viewState = getViewState(reactTag); - if (viewState.mCurrentProps == null) { - throw new IllegalStateException( - "Can not update local data to view without props: " + reactTag); - } - if (viewState.mCurrentLocalData != null - && newLocalData.hasKey("hash") - && viewState.mCurrentLocalData.getDouble("hash") == newLocalData.getDouble("hash") - && viewState.mCurrentLocalData.equals(newLocalData)) { - return; - } - viewState.mCurrentLocalData = newLocalData; - - ViewManager viewManager = viewState.mViewManager; - - if (viewManager == null) { - throw new IllegalStateException("Unable to find ViewManager for view: " + viewState); - } - Object extraData = - viewManager.updateLocalData( - viewState.mView, - viewState.mCurrentProps, - new ReactStylesDiffMap(viewState.mCurrentLocalData)); - if (extraData != null) { - viewManager.updateExtraData(viewState.mView, extraData); - } - } - @UiThread public void updateState(final int reactTag, @Nullable StateWrapper stateWrapper) { UiThreadUtil.assertOnUiThread(); @@ -519,7 +521,7 @@ public long measure( @NonNull YogaMeasureMode widthMode, float height, @NonNull YogaMeasureMode heightMode, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { return mViewManagerRegistry .get(componentName) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java index 207d79871ccd5e..5852ff1a78c643 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SendAccessibilityEvent.java @@ -8,10 +8,14 @@ package com.facebook.react.fabric.mounting.mountitems; import androidx.annotation.NonNull; +import com.facebook.react.bridge.ReactSoftException; +import com.facebook.react.bridge.RetryableMountingLayerException; import com.facebook.react.fabric.mounting.MountingManager; public class SendAccessibilityEvent implements MountItem { + private final String TAG = "Fabric.SendAccessibilityEvent"; + private final int mReactTag; private final int mEventType; @@ -22,7 +26,18 @@ public SendAccessibilityEvent(int reactTag, int eventType) { @Override public void execute(@NonNull MountingManager mountingManager) { - mountingManager.sendAccessibilityEvent(mReactTag, mEventType); + try { + mountingManager.sendAccessibilityEvent(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 + // inherently unpredictable and dangerous. If we encounter a "retryable" + // error, that is, a known category of errors that this is likely to hit + // 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); + } } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLocalDataMountItem.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLocalDataMountItem.java deleted file mode 100644 index 07104a3f5294b2..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/UpdateLocalDataMountItem.java +++ /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. - */ - -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 UpdateLocalDataMountItem implements MountItem { - - private final int mReactTag; - @NonNull private final ReadableMap mNewLocalData; - - public UpdateLocalDataMountItem(int reactTag, @NonNull ReadableMap newLocalData) { - mReactTag = reactTag; - mNewLocalData = newLocalData; - } - - @Override - public void execute(@NonNull MountingManager mountingManager) { - mountingManager.updateLocalData(mReactTag, mNewLocalData); - } - - public @NonNull ReadableMap getNewLocalData() { - return mNewLocalData; - } - - @Override - public String toString() { - StringBuilder result = - new StringBuilder("UpdateLocalDataMountItem [").append(mReactTag).append("]"); - - if (IS_DEVELOPMENT_ENVIRONMENT) { - result.append(" localData: ").append(mNewLocalData); - } - - return result.toString(); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK index 1f1f74690a4719..a1a374ef936b36 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_nat rn_android_library( name = "jscexecutor", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK b/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK index b2cdc99110b575..26444912bf4e73 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/jstasks/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "jstasks", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK b/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK index f081fc83a17774..b213c9d3133a0f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/module/annotations/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "annotations", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK b/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK index 59e700971e872b..85f23a35691b69 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/module/model/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_libra rn_android_library( name = "model", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK b/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK index b2c8041bdc6981..cc44f729d1893a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/module/processing/BUCK @@ -15,7 +15,7 @@ rn_java_annotation_processor( rn_java_library( name = "processing-lib", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], source = "8", target = "8", deps = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK index 5d0fa7d9d1c958..f12d0bf3c4b440 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "accessibilityinfo", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK index 62802072e94797..ef3d7751982482 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "appearance", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK index dc2c4d824b2a52..d54b2cd82034de 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/appregistry/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_target", "rn_android_li rn_android_library( name = "appregistry", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK index 3b873f61574438..92c4684d3014fe 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "appstate", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK index 0a4d2b472e2210..1831ff654368cb 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "blob", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK index 2ab5488c94a15f..01a57b64d3e326 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/blob/jni/BUCK @@ -11,7 +11,7 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, soname = "libreactnativeblob.$(ext)", visibility = ["PUBLIC"], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK index 01e0240e5b7dee..13384e7ac49b3d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/camera/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "camera", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK index c3fa4b7884140b..d755fa2dd9aa55 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "clipboard", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK index c8376f61beec51..47dad24dd0491a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/common/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "common", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK index 4e8a8ee3fafff2..535ee65d070cc3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "core", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK index 91683c6f233033..f4b8776d0c113e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "datepicker", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK index 2f0d2caef1a471..389c2d9681cef3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/debug/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "debug", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], @@ -30,7 +30,7 @@ rn_android_library( rn_android_library( name = "interfaces", srcs = glob(["interfaces/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK index a33ecbf2677641..65a9d8d0bb2aca 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "deviceinfo", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK index 1987e6e9223e16..b6a6e85c354448 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "dialog", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK index 5bbb74d994b22f..f88d3d22465bda 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fabric/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_target", "rn_android_li rn_android_library( name = "fabric", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ ], visibility = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK index fd6819910638ce..a0762971a030ed 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "fresco", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK index a00c306f8da8a4..2b7eeb854d9425 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "i18nmanager", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK index d924754e990d0e..706425f1bc3e8e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/image/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "image", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK index a7f538cc2ea321..e3102ed4685ca2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/intent/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "intent", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK index ccb9013abe9045..088baa4883eb36 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/network/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "network", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK index 484e374c72a94c..4943b35a537b3f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "permissions", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK index 11288aa404c14c..8bcc886c1b0652 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/share/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "share", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK index 8e199ed82a85e2..aefbb42597bfe6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/sound/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "sound", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ ], visibility = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK index 8643e041815f6a..835340443bbd44 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "statusbar", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK index 2231b08feec476..716282e68cfcba 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/storage/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "storage", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK index afc48e3db35129..89a47d4f0d2ea6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK @@ -7,7 +7,7 @@ rn_android_library( "ReactNativeVersion.java", ], is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], @@ -32,7 +32,7 @@ rn_android_library( "AndroidInfoHelpers.java", ], is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/BUCK deleted file mode 100644 index dac030819debc6..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/BUCK +++ /dev/null @@ -1,26 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "timepicker", - srcs = glob(["**/*.java"]), - is_androidx = True, - labels = ["supermodule:android/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_target("java/com/facebook/fbreact/specs:FBReactNativeSpec")], -) diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/DismissableTimePickerDialog.java b/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/DismissableTimePickerDialog.java deleted file mode 100644 index 23540757dea757..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/DismissableTimePickerDialog.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.modules.timepicker; - -import android.app.TimePickerDialog; -import android.content.Context; -import android.os.Build; -import androidx.annotation.Nullable; - -/** - * Certain versions of Android (Jellybean-KitKat) have a bug where when dismissed, the {@link - * TimePickerDialog} still calls the OnTimeSetListener. This class works around that issue by *not* - * calling super.onStop on KitKat on lower, as that would erroneously call the OnTimeSetListener - * when the dialog is dismissed, or call it twice when "OK" is pressed. - * - *

See: Issue 34833 - */ -public class DismissableTimePickerDialog extends TimePickerDialog { - - public DismissableTimePickerDialog( - Context context, - @Nullable TimePickerDialog.OnTimeSetListener callback, - int hourOfDay, - int minute, - boolean is24HourView) { - super(context, callback, hourOfDay, minute, is24HourView); - } - - public DismissableTimePickerDialog( - Context context, - int theme, - @Nullable TimePickerDialog.OnTimeSetListener callback, - int hourOfDay, - int minute, - boolean is24HourView) { - super(context, theme, callback, hourOfDay, minute, is24HourView); - } - - @Override - protected void onStop() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - super.onStop(); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogFragment.java b/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogFragment.java deleted file mode 100644 index 298e0bc14c2040..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogFragment.java +++ /dev/null @@ -1,100 +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.timepicker; - -import android.app.Dialog; -import android.app.TimePickerDialog.OnTimeSetListener; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.os.Build; -import android.os.Bundle; -import android.text.format.DateFormat; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; -import java.util.Calendar; -import java.util.Locale; - -@SuppressWarnings("ValidFragment") -public class TimePickerDialogFragment extends DialogFragment { - - @Nullable private OnTimeSetListener mOnTimeSetListener; - @Nullable private OnDismissListener mOnDismissListener; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Bundle args = getArguments(); - return createDialog(args, getActivity(), mOnTimeSetListener); - } - - /*package*/ static Dialog createDialog( - Bundle args, Context activityContext, @Nullable OnTimeSetListener onTimeSetListener) { - final Calendar now = Calendar.getInstance(); - int hour = now.get(Calendar.HOUR_OF_DAY); - int minute = now.get(Calendar.MINUTE); - boolean is24hour = DateFormat.is24HourFormat(activityContext); - - TimePickerMode mode = TimePickerMode.DEFAULT; - if (args != null && args.getString(TimePickerDialogModule.ARG_MODE, null) != null) { - mode = - TimePickerMode.valueOf( - args.getString(TimePickerDialogModule.ARG_MODE).toUpperCase(Locale.US)); - } - - if (args != null) { - hour = args.getInt(TimePickerDialogModule.ARG_HOUR, now.get(Calendar.HOUR_OF_DAY)); - minute = args.getInt(TimePickerDialogModule.ARG_MINUTE, now.get(Calendar.MINUTE)); - is24hour = - args.getBoolean( - TimePickerDialogModule.ARG_IS24HOUR, DateFormat.is24HourFormat(activityContext)); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (mode == TimePickerMode.CLOCK) { - return new DismissableTimePickerDialog( - activityContext, - activityContext - .getResources() - .getIdentifier("ClockTimePickerDialog", "style", activityContext.getPackageName()), - onTimeSetListener, - hour, - minute, - is24hour); - } else if (mode == TimePickerMode.SPINNER) { - return new DismissableTimePickerDialog( - activityContext, - activityContext - .getResources() - .getIdentifier( - "SpinnerTimePickerDialog", "style", activityContext.getPackageName()), - onTimeSetListener, - hour, - minute, - is24hour); - } - } - return new DismissableTimePickerDialog( - activityContext, onTimeSetListener, hour, minute, is24hour); - } - - @Override - public void onDismiss(DialogInterface dialog) { - super.onDismiss(dialog); - if (mOnDismissListener != null) { - mOnDismissListener.onDismiss(dialog); - } - } - - public void setOnDismissListener(@Nullable OnDismissListener onDismissListener) { - mOnDismissListener = onDismissListener; - } - - public void setOnTimeSetListener(@Nullable OnTimeSetListener onTimeSetListener) { - mOnTimeSetListener = onTimeSetListener; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogModule.java deleted file mode 100644 index b40f7b6b46c6bc..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerDialogModule.java +++ /dev/null @@ -1,136 +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.timepicker; - -import android.app.Activity; -import android.app.TimePickerDialog.OnTimeSetListener; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.os.Bundle; -import android.widget.TimePicker; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import com.facebook.fbreact.specs.NativeTimePickerAndroidSpec; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.module.annotations.ReactModule; - -/** - * {@link NativeModule} that allows JS to show a native time picker dialog and get called back when - * the user selects a time. - */ -@ReactModule(name = TimePickerDialogModule.FRAGMENT_TAG) -public class TimePickerDialogModule extends NativeTimePickerAndroidSpec { - - @VisibleForTesting public static final String FRAGMENT_TAG = "TimePickerAndroid"; - - private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY"; - - /* package */ static final String ARG_HOUR = "hour"; - /* package */ static final String ARG_MINUTE = "minute"; - /* package */ static final String ARG_IS24HOUR = "is24Hour"; - /* package */ static final String ARG_MODE = "mode"; - /* package */ static final String ACTION_TIME_SET = "timeSetAction"; - /* package */ static final String ACTION_DISMISSED = "dismissedAction"; - - public TimePickerDialogModule(ReactApplicationContext reactContext) { - super(reactContext); - } - - @Override - public String getName() { - return FRAGMENT_TAG; - } - - private class TimePickerDialogListener implements OnTimeSetListener, OnDismissListener { - - private final Promise mPromise; - private boolean mPromiseResolved = false; - - public TimePickerDialogListener(Promise promise) { - mPromise = promise; - } - - @Override - public void onTimeSet(TimePicker view, int hour, int minute) { - if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) { - WritableMap result = new WritableNativeMap(); - result.putString("action", ACTION_TIME_SET); - result.putInt("hour", hour); - result.putInt("minute", minute); - 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; - } - } - } - - @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; - - // We want to support both android.app.Activity and the pre-Honeycomb FragmentActivity - // (for apps that use it for legacy reasons). This unfortunately leads to some code duplication. - FragmentManager fragmentManager = activity.getSupportFragmentManager(); - DialogFragment oldFragment = (DialogFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG); - if (oldFragment != null) { - oldFragment.dismiss(); - } - TimePickerDialogFragment fragment = new TimePickerDialogFragment(); - if (options != null) { - Bundle args = createFragmentArguments(options); - fragment.setArguments(args); - } - TimePickerDialogListener listener = new TimePickerDialogListener(promise); - fragment.setOnDismissListener(listener); - fragment.setOnTimeSetListener(listener); - fragment.show(fragmentManager, FRAGMENT_TAG); - } - - private Bundle createFragmentArguments(ReadableMap options) { - final Bundle args = new Bundle(); - if (options.hasKey(ARG_HOUR) && !options.isNull(ARG_HOUR)) { - args.putInt(ARG_HOUR, options.getInt(ARG_HOUR)); - } - if (options.hasKey(ARG_MINUTE) && !options.isNull(ARG_MINUTE)) { - args.putInt(ARG_MINUTE, options.getInt(ARG_MINUTE)); - } - if (options.hasKey(ARG_IS24HOUR) && !options.isNull(ARG_IS24HOUR)) { - args.putBoolean(ARG_IS24HOUR, options.getBoolean(ARG_IS24HOUR)); - } - if (options.hasKey(ARG_MODE) && !options.isNull(ARG_MODE)) { - args.putString(ARG_MODE, options.getString(ARG_MODE)); - } - return args; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK index fb3b79960fe585..9cd1eaa53d005c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/toast/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "toast", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK index 219a1d36feb288..69081110ea8600 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/vibration/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "vibration", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK index 4299354a4a6d76..d80e8fd4d06646 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "websocket", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK index 1cfd88bfc35a01..3aa924ecad3ad6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/BUCK @@ -6,7 +6,7 @@ rn_android_library( ["**/*.java"], ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK b/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK index 5ff37878c58a18..c31511049ce9ea 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/processing/BUCK @@ -15,7 +15,7 @@ rn_java_annotation_processor( rn_java_library( name = "processing-lib", srcs = glob(["*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], source = "7", target = "7", deps = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK index 5f114f91e88e13..8c538aa0e96aef 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "shell", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), @@ -46,7 +46,6 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/statusbar:statusbar"), react_native_target("java/com/facebook/react/modules/storage:storage"), react_native_target("java/com/facebook/react/modules/sound:sound"), - react_native_target("java/com/facebook/react/modules/timepicker:timepicker"), react_native_target("java/com/facebook/react/modules/toast:toast"), react_native_target("java/com/facebook/react/modules/vibration:vibration"), react_native_target("java/com/facebook/react/modules/websocket:websocket"), @@ -65,7 +64,6 @@ rn_android_library( react_native_target("java/com/facebook/react/views/text/frescosupport:frescosupport"), react_native_target("java/com/facebook/react/views/textinput:textinput"), react_native_target("java/com/facebook/react/views/view:view"), - react_native_target("java/com/facebook/react/views/viewpager:viewpager"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("res:shell"), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index e5d6f99a3186b9..3cd6defd986b28 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -37,7 +37,6 @@ import com.facebook.react.modules.sound.SoundManagerModule; import com.facebook.react.modules.statusbar.StatusBarModule; import com.facebook.react.modules.storage.AsyncStorageModule; -import com.facebook.react.modules.timepicker.TimePickerDialogModule; import com.facebook.react.modules.toast.ToastModule; import com.facebook.react.modules.vibration.VibrationModule; import com.facebook.react.modules.websocket.WebSocketModule; @@ -61,7 +60,6 @@ import com.facebook.react.views.text.frescosupport.FrescoBasedReactTextInlineImageViewManager; import com.facebook.react.views.textinput.ReactTextInputManager; import com.facebook.react.views.view.ReactViewManager; -import com.facebook.react.views.viewpager.ReactViewPagerManager; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -92,7 +90,6 @@ ShareModule.class, SoundManagerModule.class, StatusBarModule.class, - TimePickerDialogModule.class, ToastModule.class, VibrationModule.class, WebSocketModule.class, @@ -155,8 +152,6 @@ public MainReactPackage(MainPackageConfig config) { return new StatusBarModule(context); case SoundManagerModule.NAME: return new SoundManagerModule(context); - case TimePickerDialogModule.FRAGMENT_TAG: - return new TimePickerDialogModule(context); case ToastModule.NAME: return new ToastModule(context); case VibrationModule.NAME: @@ -191,7 +186,6 @@ public List createViewManagers(ReactApplicationContext reactContext viewManagers.add(new ReactTextInputManager()); viewManagers.add(new ReactTextViewManager()); viewManagers.add(new ReactViewManager()); - viewManagers.add(new ReactViewPagerManager()); viewManagers.add(new ReactVirtualTextViewManager()); return viewManagers; @@ -229,7 +223,6 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { ShareModule.class, StatusBarModule.class, SoundManagerModule.class, - TimePickerDialogModule.class, ToastModule.class, VibrationModule.class, WebSocketModule.class diff --git a/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK b/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK index 7ea360411204d6..beb35ce40c1055 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/surface/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "surface", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK b/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK index c57d2e682f350d..839bd5beb44bb0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/touch/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "touch", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK index 7547027a771a8d..50e716a39a8cb6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK @@ -8,7 +8,7 @@ rn_android_library( ], exclude = ["CallInvokerHolderImpl.java"], ), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", @@ -39,7 +39,7 @@ rn_android_library( rn_android_library( name = "callinvokerholder", srcs = ["CallInvokerHolderImpl.java"], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK index 3e07e5c5355a80..ad97b93f20d94a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK @@ -6,7 +6,7 @@ rn_android_library( ["*.java"], ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK index 183194f3d47dd9..9a7eb46af4bf50 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK @@ -17,7 +17,7 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", @@ -56,7 +56,7 @@ rn_xplat_cxx_library( fbandroid_deps = [ FBJNI_TARGET, ], - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, preferred_linkage = "static", preprocessor_flags = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK index a88d679b87f4f3..09e0e6ea4ec460 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BUCK @@ -14,7 +14,7 @@ rn_android_library( ], ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), @@ -60,7 +60,7 @@ rn_android_library( "DisplayMetricsHolder.java", ], is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java index b1c531f8f607f5..f430398bacf208 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java @@ -113,4 +113,7 @@ public void setProperty(T view, String propName, @Nullable Object value) { break; } } + + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) {} } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/MatrixMathHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/MatrixMathHelper.java index 179cc32e6a1842..cf448bd58908d0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/MatrixMathHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/MatrixMathHelper.java @@ -169,10 +169,6 @@ public static void decomposeMatrix(double[] transformMatrix, MatrixDecomposition skew[0] = v3Dot(row[0], row[1]); row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]); - // Compute XY shear factor and make 2nd row orthogonal to 1st. - skew[0] = v3Dot(row[0], row[1]); - row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]); - // Now, compute Y scale and normalize 2nd row. scale[1] = v3Length(row[1]); row[1] = v3Normalize(row[1], scale[1]); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index 35c9ff92013033..749202eeaad64a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -29,6 +29,7 @@ import com.facebook.react.bridge.RetryableMountingLayerException; import com.facebook.react.bridge.SoftAssertions; import com.facebook.react.bridge.UiThreadUtil; +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; @@ -784,7 +785,13 @@ public synchronized void dispatchCommand( + commandId); } ViewManager viewManager = resolveViewManager(reactTag); - viewManager.receiveCommand(view, commandId, args); + ViewManagerDelegate delegate; + if (ReactFeatureFlags.useViewManagerDelegatesForCommands + && (delegate = viewManager.getDelegate()) != null) { + delegate.receiveCommand(view, commandId, args); + } else { + viewManager.receiveCommand(view, commandId, args); + } } /** diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java index fc13de9c5ccef7..c21eaecad3630a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java @@ -288,6 +288,8 @@ public interface ReactShadowNode { YogaValue getStyleHeight(); + float getFlex(); + void setStyleHeight(float heightPx); void setStyleHeightPercent(float percent); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java index 6c58ae2d7b93d1..18aaad08112ad5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java @@ -779,6 +779,11 @@ public void setStyleMaxHeightPercent(float percent) { mYogaNode.setMaxHeightPercent(percent); } + @Override + public float getFlex() { + return mYogaNode.getFlex(); + } + @Override public void setFlex(float flex) { mYogaNode.setFlex(flex); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java index 4eff054efe9c3b..039798dcaf321f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java @@ -10,8 +10,6 @@ import android.app.Activity; import android.content.Context; import androidx.annotation.Nullable; -import com.facebook.react.bridge.JSIModule; -import com.facebook.react.bridge.JSIModuleType; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; @@ -71,16 +69,12 @@ public boolean hasCurrentActivity() { return mSurfaceID; } - @Override - public boolean isBridgeless() { - return mReactApplicationContext.isBridgeless(); + public ReactApplicationContext getReactApplicationContext() { + return mReactApplicationContext; } @Override - public JSIModule getJSIModule(JSIModuleType moduleType) { - if (isBridgeless()) { - return mReactApplicationContext.getJSIModule(moduleType); - } - return super.getJSIModule(moduleType); + public boolean isBridgeless() { + return mReactApplicationContext.isBridgeless(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java index 16545dd1eac9fe..e7f790b0570485 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java @@ -48,6 +48,17 @@ public static void processTransform(ReadableArray transforms, double[] result) { double[] helperMatrix = sHelperMatrix.get(); MatrixMathHelper.resetIdentityMatrix(result); + // If the transforms array is actually just the matrix itself, + // copy that directly. This is for Fabric LayoutAnimations support. + // All of the stuff this Java helper does is already done in C++ in Fabric, so we + // can just use that matrix directly. + if (transforms.size() == 16 && transforms.getType(0) == ReadableType.Number) { + for (int i = 0; i < transforms.size(); i++) { + result[i] = transforms.getDouble(i); + } + return; + } + for (int transformIdx = 0, size = transforms.size(); transformIdx < size; transformIdx++) { ReadableMap transform = transforms.getMap(transformIdx); String transformType = transform.keySetIterator().nextKey(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java index c034e06830b069..bd978336c2d0c8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java @@ -22,6 +22,7 @@ import com.facebook.react.bridge.UIManager; import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.uimanager.events.EventDispatcherProvider; /** Helper class for {@link UIManager}. */ public class UIManagerHelper { @@ -43,32 +44,28 @@ private static UIManager getUIManager( ReactContext context, @UIManagerType int uiManagerType, boolean returnNullIfCatalystIsInactive) { - if (context.isBridgeless()) { - return (UIManager) context.getJSIModule(JSIModuleType.UIManager); - } else { - if (!context.hasCatalystInstance()) { - ReactSoftException.logSoftException( - "UIManagerHelper", - new ReactNoCrashSoftException( - "Cannot get UIManager because the context doesn't contain a CatalystInstance.")); + if (!context.hasCatalystInstance()) { + ReactSoftException.logSoftException( + "UIManagerHelper", + 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", + new ReactNoCrashSoftException( + "Cannot get UIManager because the context doesn't contain an active CatalystInstance.")); + if (returnNullIfCatalystIsInactive) { return null; } - // TODO T60461551: add tests to verify emission of events when the ReactContext is being turn - // down. - if (!context.hasActiveCatalystInstance()) { - ReactSoftException.logSoftException( - "UIManagerHelper", - new ReactNoCrashSoftException( - "Cannot get UIManager because the context doesn't contain an active CatalystInstance.")); - if (returnNullIfCatalystIsInactive) { - return null; - } - } - CatalystInstance catalystInstance = context.getCatalystInstance(); - return uiManagerType == FABRIC - ? (UIManager) catalystInstance.getJSIModule(JSIModuleType.UIManager) - : catalystInstance.getNativeModule(UIManagerModule.class); } + CatalystInstance catalystInstance = context.getCatalystInstance(); + return uiManagerType == FABRIC + ? (UIManager) catalystInstance.getJSIModule(JSIModuleType.UIManager) + : catalystInstance.getNativeModule(UIManagerModule.class); } /** @@ -87,6 +84,13 @@ public static EventDispatcher getEventDispatcherForReactTag(ReactContext context @Nullable public static EventDispatcher getEventDispatcher( ReactContext context, @UIManagerType int uiManagerType) { + // TODO T67518514 Clean this up once we migrate everything over to bridgeless mode + if (context.isBridgeless()) { + if (context instanceof ThemedReactContext) { + context = ((ThemedReactContext) context).getReactApplicationContext(); + } + return ((EventDispatcherProvider) context).getEventDispatcher(); + } UIManager uiManager = getUIManager(context, uiManagerType, false); return uiManager == null ? null : (EventDispatcher) uiManager.getEventDispatcher(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index d753e252a567c4..1f51e272ac9179 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -13,9 +13,7 @@ import static com.facebook.react.uimanager.common.UIManagerType.FABRIC; import android.content.ComponentCallbacks2; -import android.content.Context; import android.content.res.Configuration; -import android.media.AudioManager; import android.view.View; import androidx.annotation.Nullable; import androidx.collection.ArrayMap; @@ -36,6 +34,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableType; 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.MapBuilder; @@ -44,6 +43,7 @@ import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.uimanager.events.EventDispatcherImpl; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; @@ -119,6 +119,7 @@ public interface CustomEventNamesResolver { private final UIImplementation mUIImplementation; private final MemoryTrimCallback mMemoryTrimCallback = new MemoryTrimCallback(); private final List mListeners = new ArrayList<>(); + private final List mUIManagerListeners = new ArrayList<>(); private @Nullable Map mViewManagerConstantsCache; private volatile int mViewManagerConstantsCacheSize; @@ -156,7 +157,7 @@ public UIManagerModule( int minTimeLeftInFrameForNonBatchedOperationMs) { super(reactContext); DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext); - mEventDispatcher = new EventDispatcher(reactContext); + mEventDispatcher = new EventDispatcherImpl(reactContext); mModuleConstants = createConstants(viewManagerResolver); mCustomDirectEvents = UIManagerModuleConstants.getDirectEventTypeConstants(); mViewManagerRegistry = new ViewManagerRegistry(viewManagerResolver); @@ -178,7 +179,7 @@ public UIManagerModule( int minTimeLeftInFrameForNonBatchedOperationMs) { super(reactContext); DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext); - mEventDispatcher = new EventDispatcher(reactContext); + mEventDispatcher = new EventDispatcherImpl(reactContext); mCustomDirectEvents = MapBuilder.newHashMap(); mModuleConstants = createConstants(viewManagersList, null, mCustomDirectEvents); mViewManagerRegistry = new ViewManagerRegistry(viewManagersList); @@ -285,7 +286,8 @@ 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 * - * @param viewManagerNames + * @deprecated this method will not be available in FabricUIManager class. + * @param viewManagerNames {@link List} names of ViewManagers */ @Deprecated public void preComputeConstantsForViewManager(List viewManagerNames) { @@ -570,7 +572,7 @@ public void setChildren(int viewTag, ReadableArray childrenTags) { * This resolves to a simple {@link #manageChildren} call, but React doesn't have enough info in * JS to formulate it itself. * - * @deprecated This method will not be available in Fabric UIManager. + * @deprecated This method will not be available in Fabric UIManager class. */ @ReactMethod @Deprecated @@ -580,10 +582,10 @@ public void replaceExistingNonRootView(int oldTag, int newTag) { /** * Method which takes a container tag and then releases all subviews for that container upon - * receipt. TODO: The method name is incorrect and will be renamed, #6033872 + * receipt. * * @param containerTag the tag of the container for which the subviews must be removed - * @deprecated This method will not be available in Fabric UIManager. + * @deprecated This method will not be available in Fabric UIManager class. */ @ReactMethod @Deprecated @@ -633,7 +635,7 @@ public void measureLayout( * window which can cause unexpected results when measuring relative to things like ScrollViews * that can have offset content on the screen. * - * @deprecated This method will not be part of Fabric. + * @deprecated this method will not be available in FabricUIManager class. */ @ReactMethod @Deprecated @@ -666,7 +668,7 @@ public void findSubviewIn( /** * Check if the first shadow node is the descendant of the second shadow node * - * @deprecated This method will not be part of Fabric. + * @deprecated this method will not be available in FabricUIManager class. */ @ReactMethod @Deprecated @@ -717,17 +719,6 @@ public void dispatchCommand(int reactTag, String commandId, @Nullable ReadableAr mUIImplementation.dispatchViewManagerCommand(reactTag, commandId, commandArgs); } - /** @deprecated use {@link SoundManager#playTouchSound()} instead. */ - @ReactMethod - @Deprecated - public void playTouchSound() { - AudioManager audioManager = - (AudioManager) getReactApplicationContext().getSystemService(Context.AUDIO_SERVICE); - if (audioManager != null) { - audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); - } - } - /** * Show a PopupMenu. * @@ -805,6 +796,9 @@ public void onBatchComplete() { for (UIManagerModuleListener listener : mListeners) { listener.willDispatchViewUpdates(this); } + for (UIManagerListener listener : mUIManagerListeners) { + listener.willDispatchViewUpdates(this); + } try { mUIImplementation.dispatchViewUpdates(batchId); } finally { @@ -862,14 +856,24 @@ public void prependUIBlock(UIBlock block) { mUIImplementation.prependUIBlock(block); } + @Deprecated public void addUIManagerListener(UIManagerModuleListener listener) { mListeners.add(listener); } + @Deprecated public void removeUIManagerListener(UIManagerModuleListener listener) { mListeners.remove(listener); } + public void addUIManagerEventListener(UIManagerListener listener) { + mUIManagerListeners.add(listener); + } + + public void removeUIManagerEventListener(UIManagerListener listener) { + mUIManagerListeners.remove(listener); + } + /** * Given a reactTag from a component, find its root node tag, if possible. Otherwise, this will * return 0. If the reactTag belongs to a root node, this will return the same reactTag. diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.java index 270d0dcf650277..19163fd6c2d66d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.java @@ -7,7 +7,11 @@ package com.facebook.react.uimanager; -/** Listener used to hook into the UIManager update process. */ +/** + * Listener used to hook into the UIManager update process. Deprecated: use UIManagerListener + * instead. This will be deleted in some future release. + */ +@Deprecated public interface UIManagerModuleListener { /** * Called right before view updates are dispatched at the end of a batch. This is useful if a diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java index 0447427d3f0d30..cabed90229c86e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java @@ -273,11 +273,6 @@ public Map getNativeProps() { return ViewManagerPropertyUpdater.getNativeProps(getClass(), getShadowNodeClass()); } - public @Nullable Object updateLocalData( - @NonNull T view, ReactStylesDiffMap props, ReactStylesDiffMap localData) { - return null; - } - /** * Subclasses can implement this method to receive state updates shared between all instances of * this component type. @@ -318,7 +313,7 @@ public long measure( YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { return 0; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.java index 79193d53dbbaa3..a464a10c97e1c8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerDelegate.java @@ -9,13 +9,17 @@ import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.bridge.ReadableArray; /** * This is an interface that must be implemented by classes that wish to take over the - * responsibility of setting properties of all views managed by the view manager. + * responsibility of setting properties of all views managed by the view manager and executing view + * commands. * * @param the type of the view supported by this delegate */ public interface ViewManagerDelegate { void setProperty(T view, String propName, @Nullable Object value); + + void receiveCommand(T view, String commandName, ReadableArray args); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK b/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK index 39953cea578b08..11bc3ba33e01ef 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/annotations/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "annotations", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK b/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK index 9f54d4ad220281..d8e9532b1a6a6f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/common/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "common", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java new file mode 100644 index 00000000000000..1fce7974087b6a --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java @@ -0,0 +1,57 @@ +/* + * 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; + +/** + * A singleton class that overrides {@link EventDispatcher} with no-op methods, to be used by + * callers that expect an EventDispatcher when the instance doesn't exist. + */ +public class BlackHoleEventDispatcher implements EventDispatcher { + + private static final EventDispatcher sEventDispatcher = new BlackHoleEventDispatcher(); + + public static EventDispatcher get() { + return sEventDispatcher; + } + + private BlackHoleEventDispatcher() {} + + @Override + public void dispatchEvent(Event event) { + FLog.d( + getClass().getSimpleName(), + "Trying to emit event to JS, but the React instance isn't ready. Event: " + + event.getEventName()); + } + + @Override + public void dispatchAllEvents() {} + + @Override + public void addListener(EventDispatcherListener listener) {} + + @Override + public void removeListener(EventDispatcherListener listener) {} + + @Override + public void addBatchEventDispatchedListener(BatchEventDispatchedListener listener) {} + + @Override + public void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener) {} + + @Override + public void registerEventEmitter(int uiManagerType, RCTEventEmitter eventEmitter) {} + + @Override + public void unregisterEventEmitter(int uiManagerType) {} + + @Override + public void onCatalystInstanceDestroyed() {} +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java index 68ed01c9858c54..2ba18340977281 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcher.java @@ -7,383 +7,28 @@ package com.facebook.react.uimanager.events; -import android.util.LongSparseArray; -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.MapBuilder; -import com.facebook.react.modules.core.ChoreographerCompat; -import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.uimanager.common.UIManagerType; -import com.facebook.systrace.Systrace; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -/** - * 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 - */ -public class EventDispatcher implements LifecycleEventListener { - - private static final Comparator EVENT_COMPARATOR = - new Comparator() { - @Override - public int compare(Event lhs, Event rhs) { - if (lhs == null && rhs == null) { - return 0; - } - if (lhs == null) { - return -1; - } - if (rhs == null) { - return 1; - } - - long diff = lhs.getTimestampMs() - rhs.getTimestampMs(); - if (diff == 0) { - return 0; - } else if (diff < 0) { - return -1; - } else { - return 1; - } - } - }; - - private final Object mEventsStagingLock = new Object(); - private final Object mEventsToDispatchLock = new Object(); - private final ReactApplicationContext mReactContext; - private final LongSparseArray mEventCookieToLastEventIdx = new LongSparseArray<>(); - private final Map mEventNameToEventId = MapBuilder.newHashMap(); - private final DispatchEventsRunnable mDispatchEventsRunnable = new DispatchEventsRunnable(); - private final ArrayList mEventStaging = new ArrayList<>(); - private final ArrayList mListeners = new ArrayList<>(); - private final List mPostEventDispatchListeners = new ArrayList<>(); - private final ScheduleDispatchFrameCallback mCurrentFrameCallback = - new ScheduleDispatchFrameCallback(); - private final AtomicInteger mHasDispatchScheduledCount = new AtomicInteger(); - - private Event[] mEventsToDispatch = new Event[16]; - private int mEventsToDispatchSize = 0; - private volatile ReactEventEmitter mReactEventEmitter; - private short mNextEventTypeId = 0; - private volatile boolean mHasDispatchScheduled = false; - - public EventDispatcher(ReactApplicationContext reactContext) { - mReactContext = reactContext; - mReactContext.addLifecycleEventListener(this); - mReactEventEmitter = new ReactEventEmitter(mReactContext); - } +public interface EventDispatcher { /** Sends the given Event to JS, coalescing eligible events if JS is backed up. */ - public void dispatchEvent(Event event) { - Assertions.assertCondition(event.isInitialized(), "Dispatched event hasn't been initialized"); - - for (EventDispatcherListener listener : mListeners) { - listener.onEventDispatch(event); - } - - synchronized (mEventsStagingLock) { - mEventStaging.add(event); - Systrace.startAsyncFlow( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); - } - maybePostFrameCallbackFromNonUI(); - } - - public void dispatchAllEvents() { - maybePostFrameCallbackFromNonUI(); - } + void dispatchEvent(Event event); - 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. - } - } + void dispatchAllEvents(); /** Add a listener to this EventDispatcher. */ - public void addListener(EventDispatcherListener listener) { - mListeners.add(listener); - } + void addListener(EventDispatcherListener 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(); - } - - /** - * We use a staging data structure so that all UI events generated in a single frame are - * dispatched at once. Otherwise, a JS runnable enqueued in a previous frame could run while the - * UI thread is in the process of adding UI events and we might incorrectly send one event this - * frame and another from this frame during the next. - */ - private void moveStagedEventsToDispatchQueue() { - synchronized (mEventsStagingLock) { - synchronized (mEventsToDispatchLock) { - for (int i = 0; i < mEventStaging.size(); i++) { - Event event = mEventStaging.get(i); - - if (!event.canCoalesce()) { - addEventToEventsToDispatch(event); - continue; - } - - long eventCookie = - getEventCookie(event.getViewTag(), event.getEventName(), event.getCoalescingKey()); - - Event eventToAdd = null; - Event eventToDispose = null; - Integer lastEventIdx = mEventCookieToLastEventIdx.get(eventCookie); - - if (lastEventIdx == null) { - eventToAdd = event; - mEventCookieToLastEventIdx.put(eventCookie, mEventsToDispatchSize); - } else { - Event lastEvent = mEventsToDispatch[lastEventIdx]; - Event coalescedEvent = event.coalesce(lastEvent); - if (coalescedEvent != lastEvent) { - eventToAdd = coalescedEvent; - mEventCookieToLastEventIdx.put(eventCookie, mEventsToDispatchSize); - eventToDispose = lastEvent; - mEventsToDispatch[lastEventIdx] = null; - } else { - eventToDispose = event; - } - } - - if (eventToAdd != null) { - addEventToEventsToDispatch(eventToAdd); - } - if (eventToDispose != null) { - eventToDispose.dispose(); - } - } - } - mEventStaging.clear(); - } - } - - private long getEventCookie(int viewTag, String eventName, short coalescingKey) { - short eventTypeId; - Short eventIdObj = mEventNameToEventId.get(eventName); - if (eventIdObj != null) { - eventTypeId = eventIdObj; - } else { - eventTypeId = mNextEventTypeId++; - mEventNameToEventId.put(eventName, eventTypeId); - } - return getEventCookie(viewTag, eventTypeId, coalescingKey); - } - - private static long getEventCookie(int viewTag, short eventTypeId, short coalescingKey) { - return viewTag - | (((long) eventTypeId) & 0xffff) << 32 - | (((long) coalescingKey) & 0xffff) << 48; - } - - public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter 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(); - } - - Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ScheduleDispatchFrameCallback"); - try { - moveStagedEventsToDispatchQueue(); - - if (!mHasDispatchScheduled) { - mHasDispatchScheduled = true; - Systrace.startAsyncFlow( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "ScheduleDispatchFrameCallback", - mHasDispatchScheduledCount.get()); - mReactContext.runOnJSQueueThread(mDispatchEventsRunnable); - } - } finally { - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - } - } - - 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; - } + void removeListener(EventDispatcherListener listener); - // 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(); - } - }); - } - } - } + void addBatchEventDispatchedListener(BatchEventDispatchedListener listener); - private class DispatchEventsRunnable implements Runnable { + void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener); - @Override - public void run() { - Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "DispatchEventsRunnable"); - try { - Systrace.endAsyncFlow( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "ScheduleDispatchFrameCallback", - mHasDispatchScheduledCount.getAndIncrement()); - mHasDispatchScheduled = false; - Assertions.assertNotNull(mReactEventEmitter); - synchronized (mEventsToDispatchLock) { - if (mEventsToDispatchSize > 0) { - // We avoid allocating an array and iterator, and "sorting" if we don't need to. - // This occurs when the size of mEventsToDispatch is zero or one. - if (mEventsToDispatchSize > 1) { - Arrays.sort(mEventsToDispatch, 0, mEventsToDispatchSize, EVENT_COMPARATOR); - } - for (int eventIdx = 0; eventIdx < mEventsToDispatchSize; eventIdx++) { - Event event = mEventsToDispatch[eventIdx]; - // Event can be null if it has been coalesced into another event. - if (event == null) { - continue; - } - Systrace.endAsyncFlow( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); - event.dispatch(mReactEventEmitter); - event.dispose(); - } - clearEventsToDispatch(); - mEventCookieToLastEventIdx.clear(); - } - } - for (BatchEventDispatchedListener listener : mPostEventDispatchListeners) { - listener.onBatchEventDispatched(); - } - } finally { - Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); - } - } - } + void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter); - private void addEventToEventsToDispatch(Event event) { - if (mEventsToDispatchSize == mEventsToDispatch.length) { - mEventsToDispatch = Arrays.copyOf(mEventsToDispatch, 2 * mEventsToDispatch.length); - } - mEventsToDispatch[mEventsToDispatchSize++] = event; - } + void unregisterEventEmitter(@UIManagerType int uiManagerType); - private void clearEventsToDispatch() { - Arrays.fill(mEventsToDispatch, 0, mEventsToDispatchSize, null); - mEventsToDispatchSize = 0; - } + void onCatalystInstanceDestroyed(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java new file mode 100644 index 00000000000000..542f03616f1d33 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherImpl.java @@ -0,0 +1,389 @@ +/* + * 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 android.util.LongSparseArray; +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.MapBuilder; +import com.facebook.react.modules.core.ChoreographerCompat; +import com.facebook.react.modules.core.ReactChoreographer; +import com.facebook.react.uimanager.common.UIManagerType; +import com.facebook.systrace.Systrace; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 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 + */ +public class EventDispatcherImpl implements EventDispatcher, LifecycleEventListener { + + private static final Comparator EVENT_COMPARATOR = + new Comparator() { + @Override + public int compare(Event lhs, Event rhs) { + if (lhs == null && rhs == null) { + return 0; + } + if (lhs == null) { + return -1; + } + if (rhs == null) { + return 1; + } + + long diff = lhs.getTimestampMs() - rhs.getTimestampMs(); + if (diff == 0) { + return 0; + } else if (diff < 0) { + return -1; + } else { + return 1; + } + } + }; + + private final Object mEventsStagingLock = new Object(); + private final Object mEventsToDispatchLock = new Object(); + private final ReactApplicationContext mReactContext; + private final LongSparseArray mEventCookieToLastEventIdx = new LongSparseArray<>(); + private final Map mEventNameToEventId = MapBuilder.newHashMap(); + private final DispatchEventsRunnable mDispatchEventsRunnable = new DispatchEventsRunnable(); + private final ArrayList mEventStaging = new ArrayList<>(); + private final ArrayList mListeners = new ArrayList<>(); + private final List mPostEventDispatchListeners = new ArrayList<>(); + private final ScheduleDispatchFrameCallback mCurrentFrameCallback = + new ScheduleDispatchFrameCallback(); + private final AtomicInteger mHasDispatchScheduledCount = new AtomicInteger(); + + private Event[] mEventsToDispatch = new Event[16]; + private int mEventsToDispatchSize = 0; + private volatile ReactEventEmitter mReactEventEmitter; + private short mNextEventTypeId = 0; + private volatile boolean mHasDispatchScheduled = false; + + public EventDispatcherImpl(ReactApplicationContext reactContext) { + mReactContext = reactContext; + mReactContext.addLifecycleEventListener(this); + mReactEventEmitter = new ReactEventEmitter(mReactContext); + } + + /** Sends the given Event to JS, coalescing eligible events if JS is backed up. */ + public void dispatchEvent(Event event) { + Assertions.assertCondition(event.isInitialized(), "Dispatched event hasn't been initialized"); + + for (EventDispatcherListener listener : mListeners) { + listener.onEventDispatch(event); + } + + synchronized (mEventsStagingLock) { + mEventStaging.add(event); + Systrace.startAsyncFlow( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); + } + maybePostFrameCallbackFromNonUI(); + } + + 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(); + } + + /** + * We use a staging data structure so that all UI events generated in a single frame are + * dispatched at once. Otherwise, a JS runnable enqueued in a previous frame could run while the + * UI thread is in the process of adding UI events and we might incorrectly send one event this + * frame and another from this frame during the next. + */ + private void moveStagedEventsToDispatchQueue() { + synchronized (mEventsStagingLock) { + synchronized (mEventsToDispatchLock) { + for (int i = 0; i < mEventStaging.size(); i++) { + Event event = mEventStaging.get(i); + + if (!event.canCoalesce()) { + addEventToEventsToDispatch(event); + continue; + } + + long eventCookie = + getEventCookie(event.getViewTag(), event.getEventName(), event.getCoalescingKey()); + + Event eventToAdd = null; + Event eventToDispose = null; + Integer lastEventIdx = mEventCookieToLastEventIdx.get(eventCookie); + + if (lastEventIdx == null) { + eventToAdd = event; + mEventCookieToLastEventIdx.put(eventCookie, mEventsToDispatchSize); + } else { + Event lastEvent = mEventsToDispatch[lastEventIdx]; + Event coalescedEvent = event.coalesce(lastEvent); + if (coalescedEvent != lastEvent) { + eventToAdd = coalescedEvent; + mEventCookieToLastEventIdx.put(eventCookie, mEventsToDispatchSize); + eventToDispose = lastEvent; + mEventsToDispatch[lastEventIdx] = null; + } else { + eventToDispose = event; + } + } + + if (eventToAdd != null) { + addEventToEventsToDispatch(eventToAdd); + } + if (eventToDispose != null) { + eventToDispose.dispose(); + } + } + } + mEventStaging.clear(); + } + } + + private long getEventCookie(int viewTag, String eventName, short coalescingKey) { + short eventTypeId; + Short eventIdObj = mEventNameToEventId.get(eventName); + if (eventIdObj != null) { + eventTypeId = eventIdObj; + } else { + eventTypeId = mNextEventTypeId++; + mEventNameToEventId.put(eventName, eventTypeId); + } + return getEventCookie(viewTag, eventTypeId, coalescingKey); + } + + private static long getEventCookie(int viewTag, short eventTypeId, short coalescingKey) { + return viewTag + | (((long) eventTypeId) & 0xffff) << 32 + | (((long) coalescingKey) & 0xffff) << 48; + } + + public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter 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(); + } + + Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ScheduleDispatchFrameCallback"); + try { + moveStagedEventsToDispatchQueue(); + + if (!mHasDispatchScheduled) { + mHasDispatchScheduled = true; + Systrace.startAsyncFlow( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "ScheduleDispatchFrameCallback", + mHasDispatchScheduledCount.get()); + mReactContext.runOnJSQueueThread(mDispatchEventsRunnable); + } + } finally { + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } + } + + 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(); + } + }); + } + } + } + + private class DispatchEventsRunnable implements Runnable { + + @Override + public void run() { + Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "DispatchEventsRunnable"); + try { + Systrace.endAsyncFlow( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "ScheduleDispatchFrameCallback", + mHasDispatchScheduledCount.getAndIncrement()); + mHasDispatchScheduled = false; + Assertions.assertNotNull(mReactEventEmitter); + synchronized (mEventsToDispatchLock) { + if (mEventsToDispatchSize > 0) { + // We avoid allocating an array and iterator, and "sorting" if we don't need to. + // This occurs when the size of mEventsToDispatch is zero or one. + if (mEventsToDispatchSize > 1) { + Arrays.sort(mEventsToDispatch, 0, mEventsToDispatchSize, EVENT_COMPARATOR); + } + for (int eventIdx = 0; eventIdx < mEventsToDispatchSize; eventIdx++) { + Event event = mEventsToDispatch[eventIdx]; + // Event can be null if it has been coalesced into another event. + if (event == null) { + continue; + } + Systrace.endAsyncFlow( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID()); + event.dispatch(mReactEventEmitter); + event.dispose(); + } + clearEventsToDispatch(); + mEventCookieToLastEventIdx.clear(); + } + } + for (BatchEventDispatchedListener listener : mPostEventDispatchListeners) { + listener.onBatchEventDispatched(); + } + } finally { + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + } + } + } + + private void addEventToEventsToDispatch(Event event) { + if (mEventsToDispatchSize == mEventsToDispatch.length) { + mEventsToDispatch = Arrays.copyOf(mEventsToDispatch, 2 * mEventsToDispatch.length); + } + mEventsToDispatch[mEventsToDispatchSize++] = event; + } + + private void clearEventsToDispatch() { + Arrays.fill(mEventsToDispatch, 0, mEventsToDispatchSize, null); + mEventsToDispatchSize = 0; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.java new file mode 100644 index 00000000000000..6f6537c068cf50 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.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.uimanager.events; + +/** + * An interface that can be implemented by a {@link com.facebook.react.bridge.ReactContext} to + * provide a first-class API for accessing the {@link EventDispatcher} from the {@link + * com.facebook.react.bridge.UIManager}. + */ +public interface EventDispatcherProvider { + + /** + * This method should always return an EventDispatcher, even if the instance doesn't exist; in + * that case it should return the empty {@link BlackHoleEventDispatcher}. + * + * @return An {@link EventDispatcher} to emit events to JS. + */ + EventDispatcher getEventDispatcher(); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK b/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK index 1f67fb5365cf5e..6d31563ef3a53f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/util/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "util", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/util/BUCK b/ReactAndroid/src/main/java/com/facebook/react/util/BUCK index 84188101bbfc06..679245155e108d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/util/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/util/BUCK @@ -3,7 +3,7 @@ load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_tar rn_android_library( name = "util", srcs = glob(["**/*.java"]), - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java index 5985c4ff1b946d..aee5f3f9c94820 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate.java @@ -47,13 +47,14 @@ public void setProperty(T view, String propName, @Nullable Object value) { } } - public void receiveCommand(AndroidDrawerLayoutManagerInterface viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case "openDrawer": - viewManager.openDrawer(view); + mViewManager.openDrawer(view); break; case "closeDrawer": - viewManager.closeDrawer(view); + mViewManager.closeDrawer(view); break; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java index 56a18f9aba0661..e5fc3ac7a2c2c2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate.java @@ -47,10 +47,11 @@ public void setProperty(T view, String propName, @Nullable Object value) { } } - public void receiveCommand(AndroidSwipeRefreshLayoutManagerInterface viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case "setNativeRefreshing": - viewManager.setNativeRefreshing(view, args.getBoolean(0)); + mViewManager.setNativeRefreshing(view, args.getBoolean(0)); break; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java index 5152015a9565b0..1b2f225d390ae7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate.java @@ -56,10 +56,11 @@ public void setProperty(T view, String propName, @Nullable Object value) { } } - public void receiveCommand(AndroidSwitchManagerInterface viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case "setNativeValue": - viewManager.setNativeValue(view, args.getBoolean(0)); + mViewManager.setNativeValue(view, args.getBoolean(0)); break; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java index 2f7b5d0853d12e..68c110b32412e8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/AndroidViewPagerManagerDelegate.java @@ -43,13 +43,14 @@ public void setProperty(T view, String propName, @Nullable Object value) { } } - public void receiveCommand(AndroidViewPagerManagerInterface viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case "setPage": - viewManager.setPage(view, args.getInt(0)); + mViewManager.setPage(view, args.getInt(0)); break; case "setPageWithoutAnimation": - viewManager.setPageWithoutAnimation(view, args.getInt(0)); + mViewManager.setPageWithoutAnimation(view, args.getInt(0)); break; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK index d950d48939265a..039099fb0d634a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "viewmanagers", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java index 5e419f8bd3fe91..65d557ced192eb 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/SwitchManagerDelegate.java @@ -15,7 +15,6 @@ 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 SwitchManagerDelegate & SwitchManagerInterface> extends BaseViewManagerDelegate { public SwitchManagerDelegate(U viewManager) { @@ -53,10 +52,11 @@ public void setProperty(T view, String propName, @Nullable Object value) { } } - public void receiveCommand(SwitchManagerInterface viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case "setValue": - viewManager.setValue(view, args.getBoolean(0)); + mViewManager.setValue(view, args.getBoolean(0)); break; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK index c9f4f1bda7ce78..2b6f2c1e932a8e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/common/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "common", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/android/androidx:annotation"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK index 9b24b8e6611515..ac71cc153ec774 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "drawer", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK index e60547c2a4101b..663357ac133d7f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK @@ -8,7 +8,7 @@ rn_android_library( name = "imageevents", srcs = IMAGE_EVENT_FILES, is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), @@ -32,7 +32,7 @@ rn_android_library( exclude = IMAGE_EVENT_FILES, ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK index 990f459c89c89a..4e9cafe129a021 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/imagehelper/BUCK @@ -7,7 +7,7 @@ rn_android_library( exclude = ["MultiSourceHelper.java"], ), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], @@ -22,7 +22,7 @@ rn_android_library( name = "withmultisource", srcs = ["MultiSourceHelper.java"], is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK index 6c69eb6af88884..53e6a5a987f584 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "modal", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK index ca04eca2315179..00b41242092acb 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "picker", srcs = glob(["**/*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK index 6ef5436e19a58a..af7909efc1e8ee 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "progressbar", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK index ed41f3c10ebe43..736a73dc537560 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "scroll", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 1b2c5312381c47..c6db70340f8ff9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -91,6 +91,9 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private int mFinalAnimatedPositionScrollX = 0; private int mFinalAnimatedPositionScrollY = 0; + private int mLastStateUpdateScrollX = -1; + private int mLastStateUpdateScrollY = -1; + private final Rect mTempRect = new Rect(); public ReactHorizontalScrollView(Context context) { @@ -409,7 +412,7 @@ public boolean onTouchEvent(MotionEvent ev) { mVelocityHelper.calculateVelocity(ev); int action = ev.getAction() & MotionEvent.ACTION_MASK; if (action == MotionEvent.ACTION_UP && mDragging) { - updateStateOnScroll(getScrollX(), getScrollY()); + updateStateOnScroll(); float velocityX = mVelocityHelper.getXVelocity(); float velocityY = mVelocityHelper.getYVelocity(); @@ -604,11 +607,6 @@ 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 we aren't going to do anything (send events or snap to page), we can early exit out. - if (!mSendMomentumEvents && !mPagingEnabled && !isScrollPerfLoggingEnabled()) { - return; - } - // 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) { @@ -624,16 +622,29 @@ private void handlePostTouchScrolling(int velocityX, int velocityY) { new Runnable() { private boolean mSnappingToPage = false; + private boolean mRunning = true; + private int mStableFrames = 0; @Override public void run() { if (mActivelyScrolling) { - // We are still scrolling so we just post to check again a frame later + // We are still scrolling. mActivelyScrolling = false; - ViewCompat.postOnAnimationDelayed( - ReactHorizontalScrollView.this, this, ReactScrollViewHelper.MOMENTUM_DELAY); + mStableFrames = 0; + mRunning = true; } else { - updateStateOnScroll(getScrollX(), getScrollY()); + // There has not been a scroll update since the last time this Runnable executed. + updateStateOnScroll(); + + // We keep checking for updates until the ScrollView has "stabilized" and hasn't + // scrolled for N consecutive frames. This number is arbitrary: big enough to catch + // a number of race conditions, but small enough to not cause perf regressions, etc. + // In anecdotal testing, it seemed like a decent number. + // Without this check, sometimes this Runnable stops executing too soon - it will + // fire before the first scroll event of an animated scroll/fling, and stop + // immediately. + mStableFrames++; + mRunning = (mStableFrames < 3); if (mPagingEnabled && !mSnappingToPage) { // Only if we have pagingEnabled and we have not snapped to the page do we @@ -647,14 +658,21 @@ public void run() { if (mSendMomentumEvents) { ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactHorizontalScrollView.this); } - ReactHorizontalScrollView.this.mPostTouchRunnable = null; disableFpsListener(); } } + + // We are still scrolling so we just post to check again a frame later + if (mRunning) { + ViewCompat.postOnAnimationDelayed( + ReactHorizontalScrollView.this, this, ReactScrollViewHelper.MOMENTUM_DELAY); + } else { + mPostTouchRunnable = null; + } } }; ViewCompat.postOnAnimationDelayed( - ReactHorizontalScrollView.this, mPostTouchRunnable, ReactScrollViewHelper.MOMENTUM_DELAY); + this, mPostTouchRunnable, ReactScrollViewHelper.MOMENTUM_DELAY); } /** Get current X position or position after current animation finishes, if any. */ @@ -960,7 +978,7 @@ public void reactSmoothScrollTo(int x, int y) { public void onAnimationUpdate(ValueAnimator valueAnimator) { int scrollValueX = (Integer) valueAnimator.getAnimatedValue("scrollX"); int scrollValueY = (Integer) valueAnimator.getAnimatedValue("scrollY"); - ReactHorizontalScrollView.this.scrollTo(scrollValueX, scrollValueY); + scrollTo(scrollValueX, scrollValueY); } }); mScrollAnimator.addListener( @@ -973,6 +991,7 @@ public void onAnimationEnd(Animator animator) { mFinalAnimatedPositionScrollX = -1; mFinalAnimatedPositionScrollY = -1; mScrollAnimator = null; + updateStateOnScroll(); } @Override @@ -1031,10 +1050,22 @@ private void updateStateOnScroll(int scrollX, int scrollY) { return; } + // Dedupe events to reduce JNI traffic + if (scrollX == mLastStateUpdateScrollX && scrollY == mLastStateUpdateScrollY) { + return; + } + + mLastStateUpdateScrollX = scrollX; + mLastStateUpdateScrollY = scrollY; + WritableMap map = new WritableNativeMap(); map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); mStateWrapper.updateState(map); } + + private void updateStateOnScroll() { + updateStateOnScroll(getScrollX(), getScrollY()); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 7a77b954415cda..5016bafe25e4ee 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -96,6 +96,9 @@ public class ReactScrollView extends ScrollView private int mFinalAnimatedPositionScrollX; private int mFinalAnimatedPositionScrollY; + private int mLastStateUpdateScrollX = -1; + private int mLastStateUpdateScrollY = -1; + public ReactScrollView(ReactContext context) { this(context, null); } @@ -318,7 +321,7 @@ public boolean onTouchEvent(MotionEvent ev) { mVelocityHelper.calculateVelocity(ev); int action = ev.getAction() & MotionEvent.ACTION_MASK; if (action == MotionEvent.ACTION_UP && mDragging) { - updateStateOnScroll(getScrollX(), getScrollY()); + updateStateOnScroll(); float velocityX = mVelocityHelper.getXVelocity(); float velocityY = mVelocityHelper.getYVelocity(); @@ -491,11 +494,6 @@ 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 we aren't going to do anything (send events or snap to page), we can early exit out. - if (!mSendMomentumEvents && !mPagingEnabled && !isScrollPerfLoggingEnabled()) { - return; - } - // 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) { @@ -512,16 +510,29 @@ private void handlePostTouchScrolling(int velocityX, int velocityY) { new Runnable() { private boolean mSnappingToPage = false; + private boolean mRunning = true; + private int mStableFrames = 0; @Override public void run() { if (mActivelyScrolling) { - // We are still scrolling so we just post to check again a frame later + // We are still scrolling. mActivelyScrolling = false; - ViewCompat.postOnAnimationDelayed( - ReactScrollView.this, this, ReactScrollViewHelper.MOMENTUM_DELAY); + mStableFrames = 0; + mRunning = true; } else { - updateStateOnScroll(getScrollX(), getScrollY()); + // There has not been a scroll update since the last time this Runnable executed. + updateStateOnScroll(); + + // We keep checking for updates until the ScrollView has "stabilized" and hasn't + // scrolled for N consecutive frames. This number is arbitrary: big enough to catch + // a number of race conditions, but small enough to not cause perf regressions, etc. + // In anecdotal testing, it seemed like a decent number. + // Without this check, sometimes this Runnable stops executing too soon - it will + // fire before the first scroll event of an animated scroll/fling, and stop + // immediately. + mStableFrames++; + mRunning = (mStableFrames < 3); if (mPagingEnabled && !mSnappingToPage) { // Only if we have pagingEnabled and we have not snapped to the page do we @@ -535,14 +546,21 @@ public void run() { if (mSendMomentumEvents) { ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactScrollView.this); } - ReactScrollView.this.mPostTouchRunnable = null; disableFpsListener(); } } + + // We are still scrolling so we just post to check again a frame later + if (mRunning) { + ViewCompat.postOnAnimationDelayed( + ReactScrollView.this, this, ReactScrollViewHelper.MOMENTUM_DELAY); + } else { + mPostTouchRunnable = null; + } } }; ViewCompat.postOnAnimationDelayed( - ReactScrollView.this, mPostTouchRunnable, ReactScrollViewHelper.MOMENTUM_DELAY); + this, mPostTouchRunnable, ReactScrollViewHelper.MOMENTUM_DELAY); } /** Get current X position or position after current animation finishes, if any. */ @@ -831,7 +849,7 @@ public void reactSmoothScrollTo(int x, int y) { public void onAnimationUpdate(ValueAnimator valueAnimator) { int scrollValueX = (Integer) valueAnimator.getAnimatedValue("scrollX"); int scrollValueY = (Integer) valueAnimator.getAnimatedValue("scrollY"); - ReactScrollView.this.scrollTo(scrollValueX, scrollValueY); + scrollTo(scrollValueX, scrollValueY); } }); mScrollAnimator.addListener( @@ -844,6 +862,7 @@ public void onAnimationEnd(Animator animator) { mFinalAnimatedPositionScrollX = -1; mFinalAnimatedPositionScrollY = -1; mScrollAnimator = null; + updateStateOnScroll(); } @Override @@ -954,10 +973,22 @@ private void updateStateOnScroll(int scrollX, int scrollY) { return; } + // Dedupe events to reduce JNI traffic + if (scrollX == mLastStateUpdateScrollX && scrollY == mLastStateUpdateScrollY) { + return; + } + + mLastStateUpdateScrollX = scrollX; + mLastStateUpdateScrollY = scrollY; + WritableMap map = new WritableNativeMap(); map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); mStateWrapper.updateState(map); } + + private void updateStateOnScroll() { + updateStateOnScroll(getScrollX(), getScrollY()); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK index 4aa48da0029ddf..63f1658e470b6f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "slider", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java index 2bab0c053204a3..1cc148fe0d2605 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java @@ -259,7 +259,7 @@ public long measure( YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { SeekBar reactSlider = new ReactSlider(context, null, STYLE); final int spec = View.MeasureSpec.makeMeasureSpec( diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK index f4e77eb6322462..528c4af8a55056 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "swiperefresh", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index 5d7a4343db66d4..c8462a138ccdc5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -131,7 +131,7 @@ public void setProgressViewOffset(final ReactSwipeRefreshLayout view, final floa @Override public void setNativeRefreshing(ReactSwipeRefreshLayout view, boolean value) { - // TODO(T52835863): Implement when view commands start using delegates generated by JS. + setRefreshing(view, value); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK index 677c0ddb179f57..cd59cc2b720430 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "switchview", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/android/androidx:appcompat"), react_native_dep("third-party/android/androidx:core"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java index 0a6b8c8762e740..c2196f3ff142dc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitchManager.java @@ -177,7 +177,7 @@ public void setTrackTintColor(ReactSwitch view, @Nullable Integer color) { @Override public void setNativeValue(ReactSwitch view, boolean value) { - // TODO(T52835863): Implement when view commands start using delegates generated by JS. + setValueInternal(view, value); } @Override @@ -210,7 +210,7 @@ public long measure( YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { ReactSwitch view = new ReactSwitch(context); view.setShowText(false); int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK index 6b4ef8857c33a5..cd966af069962c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "text", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index c3e248bc33b309..7984406e0e7d8f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -120,7 +120,7 @@ public long measure( YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { return TextLayoutManager.measureText( context, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index f98035c179bba1..8bde57f62d7c10 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -20,7 +20,9 @@ import android.text.TextPaint; import android.util.LayoutDirection; import android.util.LruCache; +import android.view.View; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.PixelUtil; @@ -35,6 +37,11 @@ /** Class responsible of creating {@link Spanned} object for the JS representation of Text */ public class TextLayoutManager { + // TODO T67606397: Refactor configuration of fabric logs + private static final boolean ENABLE_MEASURE_LOGGING = false; + + private static final String TAG = "TextLayoutManager"; + // 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. @@ -80,7 +87,7 @@ private static void buildSpannableFromFragment( sb.append(TextTransform.apply(fragment.getString("string"), textAttributes.mTextTransform)); int end = sb.length(); - int reactTag = fragment.getInt("reactTag"); + int reactTag = fragment.hasKey("reactTag") ? fragment.getInt("reactTag") : View.NO_ID; if (fragment.hasKey(ViewProps.IS_ATTACHMENT) && fragment.getBoolean(ViewProps.IS_ATTACHMENT)) { float width = PixelUtil.toPixelFromSP(fragment.getDouble(ViewProps.WIDTH)); @@ -216,7 +223,7 @@ public static long measureText( float height, YogaMeasureMode heightYogaMeasureMode, ReactTextViewManagerCallback reactTextViewManagerCallback, - @Nullable int[] attachmentsPositions) { + @Nullable float[] attachmentsPositions) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) TextPaint textPaint = sTextPaintInstance; @@ -234,6 +241,7 @@ public static long measureText( 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; @@ -412,16 +420,34 @@ public static long measureText( // The attachment array returns the positions of each of the attachments as attachmentsPositions[attachmentPosition] = - (int) Math.ceil(PixelUtil.toSPFromPixel(placeholderTopPosition)); + PixelUtil.toSPFromPixel(placeholderTopPosition); attachmentsPositions[attachmentPosition + 1] = - (int) Math.ceil(PixelUtil.toSPFromPixel(placeholderLeftPosition)); + PixelUtil.toSPFromPixel(placeholderLeftPosition); attachmentIndex++; } } } - return YogaMeasureOutput.make( - PixelUtil.toSPFromPixel(calculatedWidth), PixelUtil.toSPFromPixel(calculatedHeight)); + float widthInSP = PixelUtil.toSPFromPixel(calculatedWidth); + float heightInSP = PixelUtil.toSPFromPixel(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); } // TODO T31905686: This class should be private diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK index d882979b4893af..fef31ebea6c4aa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "frescosupport", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK index ba19532ffc7fa1..3b408b3d319e69 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "textinput", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK index a4d7b073035547..24d0a3f2d916fa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "unimplementedview", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + 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"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK index 1b9b8e05181547..66386a0ffbfd5c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/BUCK @@ -4,7 +4,7 @@ rn_android_library( name = "view", srcs = glob(["*.java"]), is_androidx = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], provided_deps = [ react_native_dep("third-party/android/androidx:core"), react_native_dep("third-party/android/androidx:fragment"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java index add5f38812baa0..4fb23c95a960f2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java @@ -37,15 +37,10 @@ public static Drawable createDrawableFromJSDescription( String type = drawableDescriptionDict.getString("type"); if ("ThemeAttrAndroid".equals(type)) { String attr = drawableDescriptionDict.getString("attribute"); - SoftAssertions.assertNotNull(attr); - int attrID = context.getResources().getIdentifier(attr, "attr", "android"); - if (attrID == 0) { + int attrId = getAttrId(context, attr); + if (!context.getTheme().resolveAttribute(attrId, sResolveOutValue, true)) { throw new JSApplicationIllegalArgumentException( - "Attribute " + attr + " couldn't be found in the resource list"); - } - if (!context.getTheme().resolveAttribute(attrID, sResolveOutValue, true)) { - throw new JSApplicationIllegalArgumentException( - "Attribute " + attr + " couldn't be resolved into a drawable"); + "Attribute " + attr + " with id " + attrId + " couldn't be resolved into a drawable"); } Drawable drawable = getDefaultThemeDrawable(context); return setRadius(drawableDescriptionDict, drawable); @@ -57,6 +52,18 @@ public static Drawable createDrawableFromJSDescription( } } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static int getAttrId(Context context, String attr) { + SoftAssertions.assertNotNull(attr); + if ("selectableItemBackground".equals(attr)) { + return android.R.attr.selectableItemBackground; + } else if ("selectableItemBackgroundBorderless".equals(attr)) { + return android.R.attr.selectableItemBackgroundBorderless; + } else { + return context.getResources().getIdentifier(attr, "attr", "android"); + } + } + private static Drawable getDefaultThemeDrawable(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return context.getResources().getDrawable(sResolveOutValue.resourceId, context.getTheme()); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index beb51d86fb4e39..851ec10c6f331c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -24,7 +24,7 @@ import com.facebook.react.uimanager.PointerEvents; import com.facebook.react.uimanager.Spacing; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactPropGroup; @@ -233,14 +233,12 @@ public void setFocusable(final ReactViewGroup view, boolean focusable) { new View.OnClickListener() { @Override public void onClick(View v) { - UIManagerModule uiManager = - ((ReactContext) view.getContext()).getNativeModule(UIManagerModule.class); - - if (uiManager == null) { + final EventDispatcher mEventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag( + (ReactContext) view.getContext(), view.getId()); + if (mEventDispatcher == null) { return; } - - final EventDispatcher mEventDispatcher = uiManager.getEventDispatcher(); mEventDispatcher.dispatchEvent(new ViewGroupClickEvent(view.getId())); } }); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK deleted file mode 100644 index 77179a7e98f8f1..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK +++ /dev/null @@ -1,31 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "viewpager", - srcs = glob(["**/*.java"]), - is_androidx = True, - labels = ["supermodule:android/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"), - ], - required_for_source_only_abi = True, - visibility = [ - "PUBLIC", - ], - deps = [ - 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_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/views/scroll:scroll"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), - ], -) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java deleted file mode 100644 index 2fcc64de002273..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java +++ /dev/null @@ -1,59 +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.viewpager; - -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 {@link ReactViewPager} when user scrolls between pages (or when animating - * between pages). - * - *

Additional data provided by this event: - * - *

    - *
  • position - index of first page from the left that is currently visible - *
  • offset - value from range [0,1) describing stage between page transitions. Value x means - * that (1 - x) fraction of the page at "position" index is visible, and x fraction of the - * next page is visible. - *
- */ -/* package */ class PageScrollEvent extends Event { - - public static final String EVENT_NAME = "topPageScroll"; - - private final int mPosition; - private final float mOffset; - - protected PageScrollEvent(int viewTag, int position, float offset) { - super(viewTag); - mPosition = position; - - // folly::toJson default options don't support serialize NaN or Infinite value - mOffset = (Float.isInfinite(offset) || Float.isNaN(offset)) ? 0.0f : offset; - } - - @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); - eventData.putDouble("offset", mOffset); - return eventData; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.java deleted file mode 100644 index 68585a75ca5fd7..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.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.viewpager; - -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 {@link ReactViewPager} when user scrolling state changed. - * - *

Additional data provided by this event: - * - *

    - *
  • pageScrollState - {Idle,Dragging,Settling} - *
- */ -class PageScrollStateChangedEvent extends Event { - - public static final String EVENT_NAME = "topPageScrollStateChanged"; - - private final String mPageScrollState; - - protected PageScrollStateChangedEvent(int viewTag, String pageScrollState) { - super(viewTag); - mPageScrollState = pageScrollState; - } - - @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.putString("pageScrollState", mPageScrollState); - return eventData; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java deleted file mode 100644 index 8d59e887cb6d10..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.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.viewpager; - -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 {@link ReactViewPager} when selected page changes. - * - *

Additional data provided by this event: - * - *

    - *
  • position - index of page that has been selected - *
- */ -/* package */ class PageSelectedEvent extends Event { - - public static final String EVENT_NAME = "topPageSelected"; - - private final int mPosition; - - protected PageSelectedEvent(int viewTag, int position) { - super(viewTag); - 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/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java deleted file mode 100644 index 398751a8f6e79e..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java +++ /dev/null @@ -1,263 +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.viewpager; - -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import androidx.viewpager.widget.PagerAdapter; -import androidx.viewpager.widget.ViewPager; -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.uimanager.UIManagerModule; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.uimanager.events.NativeGestureUtil; -import java.util.ArrayList; -import java.util.List; - -/** - * Wrapper view for {@link ViewPager}. It's forwarding calls to {@link ViewGroup#addView} to add - * views to custom {@link PagerAdapter} instance which is used by {@link NativeViewHierarchyManager} - * to add children nodes according to react views hierarchy. - */ -public class ReactViewPager extends ViewPager { - - private class Adapter extends PagerAdapter { - - private final List mViews = new ArrayList<>(); - private boolean mIsViewPagerInIntentionallyInconsistentState = false; - - void addView(View child, int index) { - mViews.add(index, child); - notifyDataSetChanged(); - // This will prevent view pager from detaching views for pages that are not currently selected - // We need to do that since {@link ViewPager} relies on layout passes to position those views - // in a right way (also thanks to {@link ReactViewPagerManager#needsCustomLayoutForChildren} - // returning {@code true}). Currently we only call {@link View#measure} and - // {@link View#layout} after yoga step. - - // TODO(7323049): Remove this workaround once we figure out a way to re-layout some views on - // request - setOffscreenPageLimit(mViews.size()); - } - - void removeViewAt(int index) { - mViews.remove(index); - notifyDataSetChanged(); - - // TODO(7323049): Remove this workaround once we figure out a way to re-layout some views on - // request - setOffscreenPageLimit(mViews.size()); - } - - /** Replace a set of views to the ViewPager adapter and update the ViewPager */ - void setViews(List views) { - mViews.clear(); - mViews.addAll(views); - notifyDataSetChanged(); - - // we want to make sure we return POSITION_NONE for every view here, since this is only - // called after a removeAllViewsFromAdapter - mIsViewPagerInIntentionallyInconsistentState = false; - } - - /** - * Remove all the views from the adapter and de-parents them from the ViewPager After calling - * this, it is expected that notifyDataSetChanged should be called soon afterwards. - */ - void removeAllViewsFromAdapter(ViewPager pager) { - mViews.clear(); - pager.removeAllViews(); - // set this, so that when the next addViews is called, we return POSITION_NONE for every - // entry so we can remove whichever views we need to and add the ones that we need to. - mIsViewPagerInIntentionallyInconsistentState = true; - } - - View getViewAt(int index) { - return mViews.get(index); - } - - @Override - public int getCount() { - return mViews.size(); - } - - @Override - public int getItemPosition(Object object) { - // if we've removed all views, we want to return POSITION_NONE intentionally - return mIsViewPagerInIntentionallyInconsistentState || !mViews.contains(object) - ? POSITION_NONE - : mViews.indexOf(object); - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - View view = mViews.get(position); - container.addView(view, 0, generateDefaultLayoutParams()); - return view; - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - container.removeView((View) object); - } - - @Override - public boolean isViewFromObject(View view, Object object) { - return view == object; - } - } - - private class PageChangeListener implements ViewPager.OnPageChangeListener { - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - mEventDispatcher.dispatchEvent(new PageScrollEvent(getId(), position, positionOffset)); - } - - @Override - public void onPageSelected(int position) { - if (!mIsCurrentItemFromJs) { - mEventDispatcher.dispatchEvent(new PageSelectedEvent(getId(), position)); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - String pageScrollState; - switch (state) { - case SCROLL_STATE_IDLE: - pageScrollState = "idle"; - break; - case SCROLL_STATE_DRAGGING: - pageScrollState = "dragging"; - break; - case SCROLL_STATE_SETTLING: - pageScrollState = "settling"; - break; - default: - throw new IllegalStateException("Unsupported pageScrollState"); - } - mEventDispatcher.dispatchEvent(new PageScrollStateChangedEvent(getId(), pageScrollState)); - } - } - - private final EventDispatcher mEventDispatcher; - private boolean mIsCurrentItemFromJs; - private boolean mScrollEnabled = true; - - public ReactViewPager(ReactContext reactContext) { - super(reactContext); - mEventDispatcher = - Assertions.assertNotNull(reactContext.getNativeModule(UIManagerModule.class)) - .getEventDispatcher(); - mIsCurrentItemFromJs = false; - setOnPageChangeListener(new PageChangeListener()); - setAdapter(new Adapter()); - } - - @Override - public Adapter getAdapter() { - return (Adapter) super.getAdapter(); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!mScrollEnabled) { - return false; - } - - try { - if (super.onInterceptTouchEvent(ev)) { - NativeGestureUtil.notifyNativeGestureStarted(this, ev); - return true; - } - } catch (IllegalArgumentException e) { - // Log and ignore the error. This seems to be a bug in the android SDK and - // this is the commonly accepted workaround. - // https://tinyurl.com/mw6qkod (Stack Overflow) - FLog.w(ReactConstants.TAG, "Error intercepting touch event.", e); - } - - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (!mScrollEnabled) { - return false; - } - - try { - return super.onTouchEvent(ev); - } catch (IllegalArgumentException e) { - // Log and ignore the error. This seems to be a bug in the android SDK and - // this is the commonly accepted workaround. - // https://fburl.com/5d3iw7d9 - FLog.w(ReactConstants.TAG, "Error handling touch event.", e); - } - - return false; - } - - public void setCurrentItemFromJs(int item, boolean animated) { - mIsCurrentItemFromJs = true; - setCurrentItem(item, animated); - mIsCurrentItemFromJs = false; - } - - public void setScrollEnabled(boolean scrollEnabled) { - mScrollEnabled = scrollEnabled; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - // The viewpager reset an internal flag on this method so we need to run another layout pass - // after attaching to window. - this.requestLayout(); - post(measureAndLayout); - } - - 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()); - } - }; - - /*package*/ void addViewToAdapter(View child, int index) { - getAdapter().addView(child, index); - } - - /*package*/ void removeViewFromAdapter(int index) { - getAdapter().removeViewAt(index); - } - - /*package*/ int getViewCountInAdapter() { - return getAdapter().getCount(); - } - - /*package*/ View getViewFromAdapter(int index) { - return getAdapter().getViewAt(index); - } - - public void setViews(List views) { - getAdapter().setViews(views); - } - - public void removeAllViewsFromAdapter() { - getAdapter().removeAllViewsFromAdapter(this); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java deleted file mode 100644 index 010cdfa28728a9..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java +++ /dev/null @@ -1,175 +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.viewpager; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManagerDelegate; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.viewmanagers.AndroidViewPagerManagerDelegate; -import com.facebook.react.viewmanagers.AndroidViewPagerManagerInterface; -import java.util.Map; - -/** Instance of {@link ViewManager} that provides native {@link ViewPager} view. */ -@ReactModule(name = ReactViewPagerManager.REACT_CLASS) -public class ReactViewPagerManager extends ViewGroupManager - implements AndroidViewPagerManagerInterface { - - public static final String REACT_CLASS = "AndroidViewPager"; - - public static final int COMMAND_SET_PAGE = 1; - public static final int COMMAND_SET_PAGE_WITHOUT_ANIMATION = 2; - - private final ViewManagerDelegate mDelegate; - - public ReactViewPagerManager() { - mDelegate = new AndroidViewPagerManagerDelegate<>(this); - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - protected ReactViewPager createViewInstance(ThemedReactContext reactContext) { - return new ReactViewPager(reactContext); - } - - @Override - @ReactProp(name = "scrollEnabled", defaultBoolean = true) - public void setScrollEnabled(ReactViewPager viewPager, boolean value) { - viewPager.setScrollEnabled(value); - } - - @Override - public boolean needsCustomLayoutForChildren() { - return true; - } - - @Override - public Map getExportedCustomDirectEventTypeConstants() { - return MapBuilder.of( - PageScrollEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageScroll"), - PageScrollStateChangedEvent.EVENT_NAME, - MapBuilder.of("registrationName", "onPageScrollStateChanged"), - PageSelectedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageSelected")); - } - - @Override - public Map getCommandsMap() { - return MapBuilder.of( - "setPage", COMMAND_SET_PAGE, "setPageWithoutAnimation", COMMAND_SET_PAGE_WITHOUT_ANIMATION); - } - - @Override - public void receiveCommand( - ReactViewPager viewPager, int commandType, @Nullable ReadableArray args) { - Assertions.assertNotNull(viewPager); - Assertions.assertNotNull(args); - switch (commandType) { - case COMMAND_SET_PAGE: - { - viewPager.setCurrentItemFromJs(args.getInt(0), true); - return; - } - case COMMAND_SET_PAGE_WITHOUT_ANIMATION: - { - viewPager.setCurrentItemFromJs(args.getInt(0), false); - return; - } - default: - throw new IllegalArgumentException( - String.format( - "Unsupported command %d received by %s.", commandType, getClass().getSimpleName())); - } - } - - @Override - public void receiveCommand( - ReactViewPager viewPager, String commandType, @Nullable ReadableArray args) { - Assertions.assertNotNull(viewPager); - Assertions.assertNotNull(args); - switch (commandType) { - case "setPage": - { - viewPager.setCurrentItemFromJs(args.getInt(0), true); - return; - } - case "setPageWithoutAnimation": - { - viewPager.setCurrentItemFromJs(args.getInt(0), false); - return; - } - default: - throw new IllegalArgumentException( - String.format( - "Unsupported command %d received by %s.", commandType, getClass().getSimpleName())); - } - } - - @Override - public void addView(ReactViewPager parent, View child, int index) { - parent.addViewToAdapter(child, index); - } - - @Override - public int getChildCount(ReactViewPager parent) { - return parent.getViewCountInAdapter(); - } - - @Override - public View getChildAt(ReactViewPager parent, int index) { - return parent.getViewFromAdapter(index); - } - - @Override - public void removeViewAt(ReactViewPager parent, int index) { - parent.removeViewFromAdapter(index); - } - - @Override - @ReactProp(name = "pageMargin", defaultInt = 0) - public void setPageMargin(ReactViewPager pager, int margin) { - pager.setPageMargin((int) PixelUtil.toPixelFromDIP(margin)); - } - - @Override - @ReactProp(name = "peekEnabled", defaultBoolean = false) - public void setPeekEnabled(ReactViewPager pager, boolean peekEnabled) { - pager.setClipToPadding(!peekEnabled); - } - - @Override - public void setInitialPage(ReactViewPager view, int value) {} - - @Override - public void setKeyboardDismissMode(ReactViewPager view, @Nullable String value) {} - - @Override - public void setPage(ReactViewPager view, int page) { - // TODO(T52835863): Implement when view commands start using delegates generated by JS. - } - - @Override - public void setPageWithoutAnimation(ReactViewPager view, int page) { - // TODO(T52835863): Implement when view commands start using delegates generated by JS. - } - - @Override - public ViewManagerDelegate getDelegate() { - return mDelegate; - } -} diff --git a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp index 6e66c06ed1abe8..698ab5615de6f0 100644 --- a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp +++ b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNIVanilla.cpp @@ -388,6 +388,13 @@ static void jni_YGNodeCalculateLayoutJNI( if (throwable.get()) { env->Throw(throwable.get()); } + } catch (const std::logic_error& ex) { + env->ExceptionClear(); + jclass cl = env->FindClass("Ljava/lang/IllegalStateException;"); + static const jmethodID methodId = facebook::yoga::vanillajni::getMethodId( + env, cl, "", "(Ljava/lang/String;)V"); + auto throwable = env->NewObject(cl, methodId, env->NewStringUTF(ex.what())); + env->Throw(static_cast(throwable)); } } diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk index dabd4b7c5125c9..38a51019ee0f44 100644 --- a/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -69,6 +69,7 @@ $(call import-module,cxxreact) $(call import-module,jsi) $(call import-module,jsiexecutor) $(call import-module,callinvoker) +$(call import-module,reactperflogger) $(call import-module,hermes) $(call import-module,runtimeexecutor) diff --git a/ReactAndroid/src/main/jni/react/jni/BUCK b/ReactAndroid/src/main/jni/react/jni/BUCK index 931a8933d92986..de79bff729e9cb 100644 --- a/ReactAndroid/src/main/jni/react/jni/BUCK +++ b/ReactAndroid/src/main/jni/react/jni/BUCK @@ -43,7 +43,7 @@ rn_xplat_cxx_library( "-Wno-inconsistent-missing-override", ], fbandroid_allow_jni_merging = True, - labels = ["supermodule:android/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = ANDROID, preprocessor_flags = [ "-DLOG_TAG=\"ReactNativeJNI\"", diff --git a/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp b/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp index 0ad1f03b37b95b..e8281b8f1cbed2 100644 --- a/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp +++ b/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp @@ -50,6 +50,26 @@ std::string JavaNativeModule::getName() { return getNameMethod(wrapper_)->toStdString(); } +std::string JavaNativeModule::getSyncMethodName(unsigned int reactMethodId) { + if (reactMethodId >= syncMethods_.size()) { + throw std::invalid_argument(folly::to( + "methodId ", + reactMethodId, + " out of range [0..", + syncMethods_.size(), + "]")); + } + + auto &methodInvoker = syncMethods_[reactMethodId]; + + if (!methodInvoker.hasValue()) { + throw std::invalid_argument(folly::to( + "methodId ", reactMethodId, " is not a recognized sync method")); + } + + return methodInvoker->getMethodName(); +} + std::vector JavaNativeModule::getMethods() { std::vector ret; syncMethods_.clear(); @@ -69,6 +89,7 @@ std::vector JavaNativeModule::getMethods() { syncMethods_.begin() + methodIndex, MethodInvoker( desc->getMethod(), + methodName, desc->getSignature(), getName() + "." + methodName, true)); @@ -148,6 +169,7 @@ NewJavaNativeModule::NewJavaNativeModule( auto name = desc->getName(); methods_.emplace_back( desc->getMethod(), + desc->getName(), desc->getSignature(), moduleName + "." + name, type == "syncHook"); diff --git a/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h b/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h index 5c2d7cb9677fca..f4b46ce66b4ff7 100644 --- a/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h +++ b/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.h @@ -69,6 +69,7 @@ class JavaNativeModule : public NativeModule { messageQueueThread_(std::move(messageQueueThread)) {} std::string getName() override; + std::string getSyncMethodName(unsigned int reactMethodId) override; folly::dynamic getConstants() override; std::vector getMethods() override; void invoke(unsigned int reactMethodId, folly::dynamic &¶ms, int callId) diff --git a/ReactAndroid/src/main/jni/react/jni/MethodInvoker.cpp b/ReactAndroid/src/main/jni/react/jni/MethodInvoker.cpp index 571495b77232bb..496a99cdca3abd 100644 --- a/ReactAndroid/src/main/jni/react/jni/MethodInvoker.cpp +++ b/ReactAndroid/src/main/jni/react/jni/MethodInvoker.cpp @@ -190,10 +190,12 @@ std::size_t countJsArgs(const std::string &signature) { MethodInvoker::MethodInvoker( alias_ref method, + std::string methodName, std::string signature, std::string traceName, bool isSync) : method_(method->getMethodID()), + methodName_(methodName), signature_(signature), jsArgCount_(countJsArgs(signature) - 2), traceName_(std::move(traceName)), @@ -203,6 +205,10 @@ MethodInvoker::MethodInvoker( << "Non-sync hooks cannot have a non-void return type"; } +std::string MethodInvoker::getMethodName() const { + return methodName_; +} + MethodCallResult MethodInvoker::invoke( std::weak_ptr &instance, alias_ref module, diff --git a/ReactAndroid/src/main/jni/react/jni/MethodInvoker.h b/ReactAndroid/src/main/jni/react/jni/MethodInvoker.h index ee3eb087dddb9f..67f5fc06d637e9 100644 --- a/ReactAndroid/src/main/jni/react/jni/MethodInvoker.h +++ b/ReactAndroid/src/main/jni/react/jni/MethodInvoker.h @@ -37,6 +37,7 @@ class MethodInvoker { public: MethodInvoker( jni::alias_ref method, + std::string methodName, std::string signature, std::string traceName, bool isSync); @@ -46,12 +47,15 @@ class MethodInvoker { jni::alias_ref module, const folly::dynamic ¶ms); + std::string getMethodName() const; + bool isSyncHook() const { return isSync_; } private: jmethodID method_; + std::string methodName_; std::string signature_; std::size_t jsArgCount_; std::string traceName_; diff --git a/ReactAndroid/src/main/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK b/ReactAndroid/src/main/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK index 1473f92d1099f4..2a86bc6af780b5 100644 --- a/ReactAndroid/src/main/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK +++ b/ReactAndroid/src/main/libraries/fbcore/src/test/java/com/facebook/powermock/BUCK @@ -2,164 +2,174 @@ load("//tools/build_defs:fb_native_wrapper.bzl", "fb_native") load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library", "rn_prebuilt_jar") rn_android_library( - name = "powermock", - visibility = ["//ReactAndroid/..."], + name = "powermock2", + visibility = ["PUBLIC"], exported_deps = [ - ":javassist", - ":mockito-all", - ":powermock-api-mockito", + ":javassist-prebuilt", + ":powermock-api-mockito2", ":powermock-api-support", ":powermock-classloading-base", - ":powermock-classloading-xstream", + ":powermock-classloading-xstream-prebuilt", ":powermock-core", - ":powermock-module-junit4-rule", + ":powermock-module-junit-common-prebuilt", + ":powermock-module-junit-rule-prebuilt", ":powermock-reflect", - ":xmlpull", - ":xpp3", - ":xstream", + ":xstream-prebuilt", + ], +) + +rn_android_library( + name = "powermock-reflect", + visibility = ["PUBLIC"], + exported_deps = [ + ":byte-buddy", + ":objenesis", + ":powermock-reflect-prebuilt", ], ) rn_prebuilt_jar( - name = "powermock-core", - binary_jar = ":download-powermock-core.jar", + name = "byte-buddy", + binary_jar = ":byte-buddy-binary.jar", visibility = ["//ReactAndroid/..."], ) fb_native.remote_file( - name = "download-powermock-core.jar", - sha1 = "ea04e79244e19dcf0c3ccf6863c5b028b4b58c9c", - url = "mvn:org.powermock:powermock-core:jar:1.6.2", + name = "byte-buddy-binary.jar", + sha1 = "211a2b4d3df1eeef2a6cacf78d74a1f725e7a840", + url = "mvn:net.bytebuddy:byte-buddy:jar:1.9.10", ) rn_prebuilt_jar( - name = "powermock-api-mockito", - binary_jar = ":download-powermock-api-mockito.jar", + name = "byte-buddy-agent", + binary_jar = ":byte-buddy-agent-binary.jar", visibility = ["//ReactAndroid/..."], ) fb_native.remote_file( - name = "download-powermock-api-mockito.jar", - sha1 = "c213230ae20a7b422f3d622a261d0e3427d2464c", - url = "mvn:org.powermock:powermock-api-mockito:jar:1.6.2", + name = "byte-buddy-agent-binary.jar", + sha1 = "9674aba5ee793e54b864952b001166848da0f26b", + url = "mvn:net.bytebuddy:byte-buddy-agent:jar:1.9.10", ) rn_prebuilt_jar( - name = "powermock-api-support", - binary_jar = ":download-powermock-api-support.jar", + name = "objenesis", + binary_jar = ":objenesis-binary.jar", visibility = ["//ReactAndroid/..."], ) fb_native.remote_file( - name = "download-powermock-api-support.jar", - sha1 = "93b21413b4ee99b7bc0dd34e1416fdca96866aaf", - url = "mvn:org.powermock:powermock-api-support:jar:1.6.2", + name = "objenesis-binary.jar", + sha1 = "639033469776fd37c08358c6b92a4761feb2af4b", + url = "mvn:org.objenesis:objenesis:jar:2.6", ) rn_prebuilt_jar( - name = "powermock-module-junit4-rule", - binary_jar = ":download-powermock-module-junit4-rule.jar", - visibility = ["//ReactAndroid/..."], + name = "powermock-reflect-prebuilt", + binary_jar = ":powermock-reflect.jar", ) fb_native.remote_file( - name = "download-powermock-module-junit4-rule.jar", - sha1 = "4847638c5729b9f203e21144b0bdb5d34d888473", - url = "mvn:org.powermock:powermock-module-junit4-rule:jar:1.6.2", + name = "powermock-reflect.jar", + sha1 = "9a8b85397c5a72923962ee9e6bf774e8458803bb", + url = "mvn:org.powermock:powermock-reflect:jar:2.0.7", ) rn_prebuilt_jar( - name = "powermock-classloading-xstream", - binary_jar = ":download-powermock-classloading-xstream.jar", - visibility = ["//ReactAndroid/..."], + name = "powermock-api-mockito2", + binary_jar = ":powermock-api-mockito2.jar", ) fb_native.remote_file( - name = "download-powermock-classloading-xstream.jar", - sha1 = "3ced31cd7024fe365b9f3c8082d22c02434577da", - url = "mvn:org.powermock:powermock-classloading-xstream:jar:1.6.2", + name = "powermock-api-mockito2.jar", + sha1 = "9f40156d9f6f65c6459a65e34f3c7c4fef8b3c49", + url = "mvn:org.powermock:powermock-api-mockito2:jar:2.0.7", +) + +rn_prebuilt_jar( + name = "powermock-api-support", + binary_jar = ":powermock-api-support.jar", +) + +fb_native.remote_file( + name = "powermock-api-support.jar", + sha1 = "e311918de98f5d8b726031ca840664691599fd71", + url = "mvn:org.powermock:powermock-api-support:jar:2.0.7", ) rn_prebuilt_jar( name = "powermock-classloading-base", - binary_jar = ":download-powermock-classloading-base.jar", - visibility = ["//ReactAndroid/..."], + binary_jar = ":powermock-classloading-base.jar", ) fb_native.remote_file( - name = "download-powermock-classloading-base.jar", - sha1 = "c8bfc10731a02d3b241892cf2c334a754d473ca7", - url = "mvn:org.powermock:powermock-classloading-base:jar:1.6.2", + name = "powermock-classloading-base.jar", + sha1 = "58ae5d3087ddfee5a591131d337907401276f7d4", + url = "mvn:org.powermock:powermock-classloading-base:jar:2.0.7", ) rn_prebuilt_jar( - name = "xstream", - binary_jar = ":download-xstream.jar", - visibility = ["//ReactAndroid/..."], + name = "powermock-classloading-xstream-prebuilt", + binary_jar = ":powermock-classloading-xstream.jar", ) fb_native.remote_file( - name = "download-xstream.jar", - sha1 = "97e5013f391487cce4de6b0eebcde21549e91872", - url = "mvn:com.thoughtworks.xstream:xstream:jar:1.4.2", + name = "powermock-classloading-xstream.jar", + sha1 = "2ec4d94a584f12b0aa1165279e92ef3d5fda1b93", + url = "mvn:org.powermock:powermock-classloading-xstream:jar:2.0.7", ) rn_prebuilt_jar( - name = "powermock-reflect", - binary_jar = ":download-powermock-reflect.jar", - visibility = ["//ReactAndroid/..."], + name = "powermock-core", + binary_jar = ":powermock-core.jar", ) fb_native.remote_file( - name = "download-powermock-reflect.jar", - sha1 = "1af1bbd1207c3ecdcf64973e6f9d57dcd17cc145", - url = "mvn:org.powermock:powermock-reflect:jar:1.6.2", + name = "powermock-core.jar", + sha1 = "484c06b406c5a21a4a2ad39f6fe36a0f77834aa9", + url = "mvn:org.powermock:powermock-core:jar:2.0.7", ) rn_prebuilt_jar( - name = "javassist", - binary_jar = ":download-javassist.jar", - visibility = ["//ReactAndroid/..."], + name = "powermock-module-junit-common-prebuilt", + binary_jar = ":powermock-module-junit-common.jar", ) fb_native.remote_file( - name = "download-javassist.jar", - sha1 = "a9cbcdfb7e9f86fbc74d3afae65f2248bfbf82a0", - url = "mvn:org.javassist:javassist:jar:3.20.0-GA", + name = "powermock-module-junit-common.jar", + sha1 = "e890f92292aa525000a8fa95a8ca4015e3eb78b8", + url = "mvn:org.powermock:powermock-module-junit4-common:jar:2.0.7", ) rn_prebuilt_jar( - name = "mockito-all", - binary_jar = ":download-mockito-all.jar", - visibility = ["//ReactAndroid/..."], + name = "powermock-module-junit-rule-prebuilt", + binary_jar = ":powermock-module-junit-rule.jar", ) fb_native.remote_file( - name = "download-mockito-all.jar", - sha1 = "539df70269cc254a58cccc5d8e43286b4a73bf30", - url = "mvn:org.mockito:mockito-all:jar:1.10.19", + name = "powermock-module-junit-rule.jar", + sha1 = "d0d14709ffec2c3cbad0e3d6256bc8ace682398d", + url = "mvn:org.powermock:powermock-module-junit4-rule:jar:2.0.7", ) rn_prebuilt_jar( - name = "xmlpull", - binary_jar = ":download-xmlpull.jar", - visibility = ["//ReactAndroid/..."], + name = "javassist-prebuilt", + binary_jar = ":javassist.jar", ) fb_native.remote_file( - name = "download-xmlpull.jar", - sha1 = "2b8e230d2ab644e4ecaa94db7cdedbc40c805dfa", - url = "mvn:xmlpull:xmlpull:jar:1.1.3.1", + name = "javassist.jar", + sha1 = "f63e6aa899e15eca8fdaa402a79af4c417252213", + url = "mvn:org.javassist:javassist:jar:3.27.0-GA", ) rn_prebuilt_jar( - name = "xpp3", - binary_jar = ":download-xpp3.jar", - visibility = ["//ReactAndroid/..."], + name = "xstream-prebuilt", + binary_jar = ":xstream.jar", ) fb_native.remote_file( - name = "download-xpp3.jar", - sha1 = "19d4e90b43059058f6e056f794f0ea4030d60b86", - url = "mvn:xpp3:xpp3_min:jar:1.1.4c", + name = "xstream.jar", + sha1 = "6c120c45a8c480bb2fea5b56502e3993ddd74fd2", + url = "mvn:com.thoughtworks.xstream:xstream:jar:1.4.11.1", ) diff --git a/ReactAndroid/src/main/res/shell/values/styles.xml b/ReactAndroid/src/main/res/shell/values/styles.xml index b53a21ea4f4d34..b0418ce8525aab 100644 --- a/ReactAndroid/src/main/res/shell/values/styles.xml +++ b/ReactAndroid/src/main/res/shell/values/styles.xml @@ -27,21 +27,4 @@ calendar - - - - - - - - - diff --git a/ReactAndroid/src/main/third-party/android/androidx/BUCK b/ReactAndroid/src/main/third-party/android/androidx/BUCK index 1f1d33cbc103e2..2eec208a3e473f 100644 --- a/ReactAndroid/src/main/third-party/android/androidx/BUCK +++ b/ReactAndroid/src/main/third-party/android/androidx/BUCK @@ -281,6 +281,15 @@ fb_native.android_library( ], ) +fb_native.android_library( + name = "test-monitor", + visibility = ["PUBLIC"], + exported_deps = [ + ":annotation", + ":test-monitor-binary", + ], +) + fb_native.android_library( name = "vectordrawable", visibility = ["PUBLIC"], @@ -448,6 +457,11 @@ fb_native.android_prebuilt_aar( aar = ":swiperefreshlayout-binary-aar", ) +fb_native.android_prebuilt_aar( + name = "test-monitor-binary", + aar = ":test-monitor-binary-aar", +) + fb_native.android_prebuilt_aar( name = "vectordrawable-binary", aar = ":vectordrawable-binary-aar", @@ -625,6 +639,12 @@ fb_native.remote_file( url = "mvn:androidx.swiperefreshlayout:swiperefreshlayout:aar:1.0.0", ) +fb_native.remote_file( + name = "test-monitor-binary-aar", + sha1 = "d2f75d117c055f35c8ebbd4f96fabc2137df9e4d", + url = "mvn:androidx.test:monitor:aar:1.2.0", +) + fb_native.remote_file( name = "vectordrawable-binary-aar", sha1 = "33d1eb71849dffbad12add134a25eb63cad4a1eb", diff --git a/ReactAndroid/src/main/third-party/java/asm/BUCK b/ReactAndroid/src/main/third-party/java/asm/BUCK index c56db5422144ea..f344506a49ca34 100644 --- a/ReactAndroid/src/main/third-party/java/asm/BUCK +++ b/ReactAndroid/src/main/third-party/java/asm/BUCK @@ -21,8 +21,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "download-asm.jar", - sha1 = "2fd56467a018aafe6ec6a73ccba520be4a7e1565", - url = "mvn:org.ow2.asm:asm:jar:5.0.1", + sha1 = "d74d4ba0dee443f68fb2dcb7fcdb945a2cd89912", + url = "mvn:org.ow2.asm:asm:jar:7.0", ) rn_prebuilt_jar( @@ -33,8 +33,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "download-asm-commons.jar", - sha1 = "7b7147a390a93a14d2edfdcf3f7b0e87a0939c3e", - url = "mvn:org.ow2.asm:asm-commons:jar:5.0.1", + sha1 = "478006d07b7c561ae3a92ddc1829bca81ae0cdd1", + url = "mvn:org.ow2.asm:asm-commons:jar:7.0", ) rn_prebuilt_jar( @@ -45,8 +45,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "download-asm-tree.jar", - sha1 = "1b1e6e9d869acd704056d0a4223071a511c619e6", - url = "mvn:org.ow2.asm:asm-tree:jar:5.0.1", + sha1 = "29bc62dcb85573af6e62e5b2d735ef65966c4180", + url = "mvn:org.ow2.asm:asm-tree:jar:7.0", ) rn_prebuilt_jar( @@ -57,8 +57,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "download-asm-util.jar", - sha1 = "7c8caddfbd0b2d7b844f8fcc75175b9cb9cf4724", - url = "mvn:org.ow2.asm:asm-util:jar:5.0.1", + sha1 = "18d4d07010c24405129a6dbb0e92057f8779fb9d", + url = "mvn:org.ow2.asm:asm-util:jar:7.0", ) rn_prebuilt_jar( @@ -69,6 +69,6 @@ rn_prebuilt_jar( fb_native.remote_file( name = "download-asm-analysis.jar", - sha1 = "e286fbee48efacb4e7c175f7948d9d8b2ab52352", - url = "mvn:org.ow2.asm:asm-analysis:jar:5.0.1", + sha1 = "4b310d20d6f1c6b7197a75f1b5d69f169bc8ac1f", + url = "mvn:org.ow2.asm:asm-analysis:jar:7.0", ) diff --git a/ReactAndroid/src/main/third-party/java/mockito2/BUCK b/ReactAndroid/src/main/third-party/java/mockito2/BUCK new file mode 100644 index 00000000000000..2427938b973112 --- /dev/null +++ b/ReactAndroid/src/main/third-party/java/mockito2/BUCK @@ -0,0 +1,60 @@ +load("//tools/build_defs:fb_native_wrapper.bzl", "fb_native") +load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library", "rn_prebuilt_jar") + +rn_android_library( + name = "mockito2", + visibility = ["PUBLIC"], + exported_deps = [ + ":byte-buddy", + ":byte-buddy-agent", + ":mockito-core-prebuilt", + ":objenesis", + ], +) + +rn_prebuilt_jar( + name = "mockito-core-prebuilt", + binary_jar = ":mockito-core.jar", +) + +fb_native.remote_file( + name = "mockito-core.jar", + sha1 = "17fb1bf75af4f5a18d8dec73b3aa55f18e6fa21a", + url = "mvn:org.mockito:mockito-core:jar:2.26.0", +) + +rn_prebuilt_jar( + name = "byte-buddy", + binary_jar = ":byte-buddy-binary.jar", + visibility = ["//ReactAndroid/..."], +) + +fb_native.remote_file( + name = "byte-buddy-binary.jar", + sha1 = "211a2b4d3df1eeef2a6cacf78d74a1f725e7a840", + url = "mvn:net.bytebuddy:byte-buddy:jar:1.9.10", +) + +rn_prebuilt_jar( + name = "byte-buddy-agent", + binary_jar = ":byte-buddy-agent-binary.jar", + visibility = ["//ReactAndroid/..."], +) + +fb_native.remote_file( + name = "byte-buddy-agent-binary.jar", + sha1 = "9674aba5ee793e54b864952b001166848da0f26b", + url = "mvn:net.bytebuddy:byte-buddy-agent:jar:1.9.10", +) + +rn_prebuilt_jar( + name = "objenesis", + binary_jar = ":objenesis-binary.jar", + visibility = ["//ReactAndroid/..."], +) + +fb_native.remote_file( + name = "objenesis-binary.jar", + sha1 = "639033469776fd37c08358c6b92a4761feb2af4b", + url = "mvn:org.objenesis:objenesis:jar:2.6", +) diff --git a/ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK b/ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK new file mode 100644 index 00000000000000..aeca8ed4f5bd3a --- /dev/null +++ b/ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK @@ -0,0 +1,216 @@ +load("//tools/build_defs:fb_native_wrapper.bzl", "fb_native") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_library", "rn_prebuilt_jar") + +rn_android_library( + name = "robolectric", + visibility = ["PUBLIC"], + exported_deps = [ + ":android-all-4.1.2_r1-robolectric-r1", + ":bouncycastle", + ":guava", + ":javax-annotation-api", + ":javax-inject", + ":robolectric4-annotations-prebuilt", + ":robolectric4-junit-prebuilt", + ":robolectric4-pluginapi-prebuilt", + ":robolectric4-plugins-maven-dependency-resolver-prebuilt", + ":robolectric4-prebuilt", + ":robolectric4-resources-prebuilt", + ":robolectric4-sandbox-prebuilt", + ":robolectric4-shadowapi-prebuilt", + ":robolectric4-shadows-framework-prebuilt", + ":robolectric4-utils-prebuilt", + ":robolectric4-utils-reflector-prebuilt", + react_native_dep("third-party/java/asm:asm"), + react_native_dep("third-party/java/sqlite:sqlite"), + react_native_dep("third-party/java/junit:junit"), + react_native_dep("third-party/android/androidx:test-monitor"), + ], +) + +rn_prebuilt_jar( + name = "android-all-4.1.2_r1-robolectric-r1", # name defines filename used by robolectric in runtime + binary_jar = ":robolectric-android-all-binary.jar", + visibility = ["//ReactAndroid/..."], +) + +# This new rule will make the .jar file appear in the "right" location, +# though that may change in the future +fb_native.export_file( + name = "robolectric-android-all-binary.jar", + src = ":robolectric-android-all-binary-remote.jar", + out = "../android-all-4.1.2_r1-robolectric-r1.jar", # name defines filename used by robolectric in runtime +) + +fb_native.remote_file( + name = "robolectric-android-all-binary-remote.jar", + sha1 = "8355a2da59fe0233ca45070ca32f08da98d0b806", + url = "mvn:org.robolectric:android-all:jar:4.1.2_r1-robolectric-r1", +) + +rn_prebuilt_jar( + name = "bouncycastle", + binary_jar = ":bouncycastle-binary.jar", + visibility = ["//ReactAndroid/..."], +) + +fb_native.remote_file( + name = "bouncycastle-binary.jar", + sha1 = "2507204241ab450456bdb8e8c0a8f986e418bd99", + url = "mvn:org.bouncycastle:bcprov-jdk15on:jar:1.59", +) + +rn_prebuilt_jar( + name = "guava", + binary_jar = ":guava-binary.jar", + visibility = ["//ReactAndroid/..."], +) + +fb_native.remote_file( + name = "guava-binary.jar", + sha1 = "ef69663836b339db335fde0df06fb3cd84e3742b", + url = "mvn:com.google.guava:guava:jar:26.0-android", +) + +rn_prebuilt_jar( + name = "robolectric4-prebuilt", + binary_jar = ":robolectric4.jar", +) + +fb_native.remote_file( + name = "robolectric4.jar", + sha1 = "66e4550b96285eadcb5a45a21ad6fbe8842fa960", + url = "mvn:org.robolectric:robolectric:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-annotations-prebuilt", + binary_jar = ":robolectric4-annotations.jar", +) + +fb_native.remote_file( + name = "robolectric4-annotations.jar", + sha1 = "3db63d633be908a18db18615b594f824c034ae6d", + url = "mvn:org.robolectric:annotations:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-junit-prebuilt", + binary_jar = ":robolectric4-junit.jar", +) + +fb_native.remote_file( + name = "robolectric4-junit.jar", + sha1 = "fcafc9942e8748c8bab832b022672ca21808c492", + url = "mvn:org.robolectric:junit:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-pluginapi-prebuilt", + binary_jar = ":robolectric4-pluginapi.jar", +) + +fb_native.remote_file( + name = "robolectric4-pluginapi.jar", + sha1 = "128acea3aed3bbe36f8fde865f3a26b920237718", + url = "mvn:org.robolectric:pluginapi:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-plugins-maven-dependency-resolver-prebuilt", + binary_jar = ":robolectric4-plugins-maven-dependency-resolver.jar", +) + +fb_native.remote_file( + name = "robolectric4-plugins-maven-dependency-resolver.jar", + sha1 = "b1ea126cb80dbba0c2947be9234bbe2877ce2a09", + url = "mvn:org.robolectric:plugins-maven-dependency-resolver:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-resources-prebuilt", + binary_jar = ":robolectric4-resources.jar", +) + +fb_native.remote_file( + name = "robolectric4-resources.jar", + sha1 = "e40030b0f6808ca378bd2c803713157ee4287ea0", + url = "mvn:org.robolectric:resources:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-sandbox-prebuilt", + binary_jar = ":robolectric4-sandbox.jar", +) + +fb_native.remote_file( + name = "robolectric4-sandbox.jar", + sha1 = "2302e406aebab5f6843dbf6c2f21952fa86ec26f", + url = "mvn:org.robolectric:sandbox:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-shadowapi-prebuilt", + binary_jar = ":robolectric4-shadowapi.jar", +) + +fb_native.remote_file( + name = "robolectric4-shadowapi.jar", + sha1 = "81dfcf4a45b623b7744e46358d01c7ce054d0fff", + url = "mvn:org.robolectric:shadowapi:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-shadows-framework-prebuilt", + binary_jar = ":robolectric4-shadows-framework.jar", +) + +fb_native.remote_file( + name = "robolectric4-shadows-framework.jar", + sha1 = "150103d5732c432906f6130b734e7452855dd67b", + url = "mvn:org.robolectric:shadows-framework:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-utils-prebuilt", + binary_jar = ":robolectric4-utils.jar", +) + +fb_native.remote_file( + name = "robolectric4-utils.jar", + sha1 = "97b0331b67d0e1dc8bf50e570b6feb017f62aed1", + url = "mvn:org.robolectric:utils:jar:4.3", +) + +rn_prebuilt_jar( + name = "robolectric4-utils-reflector-prebuilt", + binary_jar = ":robolectric4-utils-reflector.jar", +) + +fb_native.remote_file( + name = "robolectric4-utils-reflector.jar", + sha1 = "3428887d068b66e33026ac533ae4647355167658", + url = "mvn:org.robolectric:utils-reflector:jar:4.3", +) + +rn_prebuilt_jar( + name = "javax-annotation-api", + binary_jar = ":javax-annotation-api.jar", +) + +fb_native.remote_file( + name = "javax-annotation-api.jar", + sha1 = "934c04d3cfef185a8008e7bf34331b79730a9d43", + url = "mvn:javax.annotation:javax.annotation-api:jar:1.3.2", +) + +rn_prebuilt_jar( + name = "javax-inject", + binary_jar = ":javax-inject.jar", +) + +fb_native.remote_file( + name = "javax-inject.jar", + sha1 = "6975da39a7040257bd51d21a231b76c915872d38", + url = "mvn:javax.inject:javax.inject:jar:1", +) diff --git a/ReactAndroid/src/main/third-party/java/robolectric3/robolectric/BUCK b/ReactAndroid/src/main/third-party/java/robolectric3/robolectric/BUCK deleted file mode 100644 index 3e35f4a81a788c..00000000000000 --- a/ReactAndroid/src/main/third-party/java/robolectric3/robolectric/BUCK +++ /dev/null @@ -1,180 +0,0 @@ -load("//tools/build_defs:fb_native_wrapper.bzl", "fb_native") -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_library", "rn_prebuilt_jar") - -rn_android_library( - name = "robolectric", - visibility = ["//ReactAndroid/..."], - exported_deps = [ - ":android-all-4.1.2_r1-robolectric-0", - ":bouncycastle", - ":icu", - ":json-20080701", - ":robolectric-annotations", - ":robolectric-core", - ":robolectric-resources", - ":robolectric-utils", - ":shadows-core-3.0-16", - ":tagsoup-1.2", - ":vtd-xml", - react_native_dep("third-party/java/asm:asm"), - react_native_dep("third-party/java/sqlite:sqlite"), - ], -) - -rn_prebuilt_jar( - name = "robolectric-core", - binary_jar = ":robolectric-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "robolectric-binary.jar", - sha1 = "f888cea3bc1a24110e315eb9827ab593610ea62f", - url = "mvn:org.robolectric:robolectric:jar:3.0", -) - -rn_prebuilt_jar( - name = "robolectric-resources", - binary_jar = ":robolectric-resources-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "robolectric-resources-binary.jar", - sha1 = "1ab609054aab67cd13a434567467f4b4774f2465", - url = "mvn:org.robolectric:robolectric-resources:jar:3.0", -) - -rn_prebuilt_jar( - name = "robolectric-annotations", - binary_jar = ":robolectric-annotations-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "robolectric-annotations-binary.jar", - sha1 = "2a6cfc072d7680694c1ff893c5dc8fec33163110", - url = "mvn:org.robolectric:robolectric-annotations:jar:3.0", -) - -rn_prebuilt_jar( - name = "robolectric-utils", - binary_jar = ":robolectric-utils-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "robolectric-utils-binary.jar", - sha1 = "4bcecd8115fe7296088bb1636e6cbd7ae8927392", - url = "mvn:org.robolectric:robolectric-utils:jar:3.0", -) - -rn_prebuilt_jar( - name = "bouncycastle", - binary_jar = ":bouncycastle-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "bouncycastle-binary.jar", - sha1 = "2507204241ab450456bdb8e8c0a8f986e418bd99", - url = "mvn:org.bouncycastle:bcprov-jdk15on:jar:1.59", -) - -rn_prebuilt_jar( - name = "vtd-xml", - binary_jar = ":vtd-xml-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "vtd-xml-binary.jar", - sha1 = "ee5bcf62c1acf76434ee9f1c67a840bafef72a6d", - url = "mvn:com.ximpleware:vtd-xml:jar:2.11", -) - -rn_prebuilt_jar( - name = "icu", - binary_jar = ":icu-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.remote_file( - name = "icu-binary.jar", - sha1 = "786d9055d4ca8c1aab4a7d4ac8283f973fd7e41f", - url = "mvn:com.ibm.icu:icu4j:jar:53.1", -) - -rn_prebuilt_jar( - name = "android-all-4.1.2_r1-robolectric-0", # name defines filename used by robolectric in runtime - binary_jar = ":robolectric-android-all-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -# This new rule will make the .jar file appear in the "right" location, -# though that may change in the future -fb_native.export_file( - name = "robolectric-android-all-binary.jar", - src = ":robolectric-android-all-binary-remote.jar", - out = "../android-all-4.1.2_r1-robolectric-0.jar", # name defines filename used by robolectric in runtime -) - -fb_native.remote_file( - name = "robolectric-android-all-binary-remote.jar", - sha1 = "aecc8ce5119a25fcea1cdf8285469c9d1261a352", - url = "mvn:org.robolectric:android-all:jar:4.1.2_r1-robolectric-0", -) - -rn_prebuilt_jar( - name = "json-20080701", # name defines filename used by robolectric in runtime - binary_jar = ":json.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.export_file( - name = "json.jar", - src = ":json-remote.jar", - out = "../json-20080701.jar", # name defines filename used by robolectric in runtime -) - -fb_native.remote_file( - name = "json-remote.jar", - sha1 = "d652f102185530c93b66158b1859f35d45687258", - url = "mvn:org.json:json:jar:20080701", -) - -rn_prebuilt_jar( - name = "tagsoup-1.2", # name defines filename used by robolectric in runtime - binary_jar = ":tagsoup.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.export_file( - name = "tagsoup.jar", - src = ":tagsoup-remote.jar", - out = "../tagsoup-1.2.jar", # name defines filename used by robolectric in runtime -) - -fb_native.remote_file( - name = "tagsoup-remote.jar", - sha1 = "639fd364750d7363c85797dc944b4a80f78fa684", - url = "mvn:org.ccil.cowan.tagsoup:tagsoup:jar:1.2", -) - -rn_prebuilt_jar( - name = "shadows-core-3.0-16", # name defines filename used by robolectric in runtime - binary_jar = ":robolectric-shadows-binary.jar", - visibility = ["//ReactAndroid/..."], -) - -fb_native.export_file( - name = "robolectric-shadows-binary.jar", - src = ":robolectric-shadows-binary-remote.jar", - out = "../shadows-core-3.0-16.jar", # name defines filename used by robolectric in runtime -) - -fb_native.remote_file( - name = "robolectric-shadows-binary-remote.jar", - sha1 = "39d7a856bf91640b1a6d044333336a2b3f3c198f", - url = "https://repo1.maven.org/maven2/org/robolectric/shadows-core/3.0/shadows-core-3.0-16.jar", -) diff --git a/ReactAndroid/src/test/java/com/facebook/react/BUCK b/ReactAndroid/src/test/java/com/facebook/react/BUCK index 3b431158943706..036fe8a98d8bfb 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/BUCK @@ -6,7 +6,6 @@ rn_robolectric_test( contacts = ["oncall+react_native@xmail.facebook.com"], deps = [ YOGA_TARGET, - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/android/androidx:core"), react_native_dep("third-party/android/androidx:fragment"), @@ -15,10 +14,8 @@ rn_robolectric_test( react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okio:okio"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), diff --git a/ReactAndroid/src/test/java/com/facebook/react/animated/BUCK b/ReactAndroid/src/test/java/com/facebook/react/animated/BUCK index 76d35a6ce1c914..c62728d5814eb9 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/animated/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/animated/BUCK @@ -9,12 +9,9 @@ rn_robolectric_test( "PUBLIC", ], deps = [ - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/animated:animated"), react_native_target("java/com/facebook/react/bridge:bridge"), diff --git a/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK b/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK index 1f5f367286672e..2721f9cb923f4a 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK @@ -1,4 +1,4 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "react_native_tests_target", "rn_android_library", "rn_robolectric_test") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_android_toplevel_dep", "react_native_dep", "react_native_target", "react_native_tests_target", "rn_android_library", "rn_robolectric_test") STANDARD_TEST_SRCS = [ "*Test.java", @@ -14,8 +14,8 @@ rn_android_library( "PUBLIC", ], deps = [ - react_native_dep("third-party/java/mockito:mockito"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), + react_native_android_toplevel_dep("third-party/java/mockito2:mockito2"), + react_native_dep("third-party/java/robolectric/4.3.1: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"), @@ -31,13 +31,10 @@ rn_robolectric_test( ], deps = [ ":testhelpers", - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"), react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), 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/uimanager:uimanager"), diff --git a/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.java index 77a46c9fee4d63..d5b89b2465147c 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.java @@ -7,13 +7,14 @@ package com.facebook.react.bridge; +import static org.mockito.Mockito.when; + import com.facebook.soloader.SoLoader; import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -56,14 +57,14 @@ private int findMethod(String mname, List me @Test(expected = NativeArgumentsParseException.class) public void testCallMethodWithoutEnoughArgs() throws Exception { int methodId = findMethod("regularMethod", mMethods); - Mockito.stub(mArguments.size()).toReturn(1); + when(mArguments.size()).thenReturn(1); mWrapper.invoke(methodId, mArguments); } @Test public void testCallMethodWithEnoughArgs() { int methodId = findMethod("regularMethod", mMethods); - Mockito.stub(mArguments.size()).toReturn(2); + when(mArguments.size()).thenReturn(2); mWrapper.invoke(methodId, mArguments); } @@ -71,14 +72,14 @@ public void testCallMethodWithEnoughArgs() { public void testCallAsyncMethodWithEnoughArgs() { // Promise block evaluates to 2 args needing to be passed from JS int methodId = findMethod("asyncMethod", mMethods); - Mockito.stub(mArguments.size()).toReturn(3); + when(mArguments.size()).thenReturn(3); mWrapper.invoke(methodId, mArguments); } @Test public void testCallSyncMethod() { int methodId = findMethod("syncMethod", mMethods); - Mockito.stub(mArguments.size()).toReturn(2); + when(mArguments.size()).thenReturn(2); mWrapper.invoke(methodId, mArguments); } diff --git a/ReactAndroid/src/test/java/com/facebook/react/devsupport/BUCK b/ReactAndroid/src/test/java/com/facebook/react/devsupport/BUCK index 2bf74a64315a33..f720bc4aaf3f4b 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/devsupport/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/devsupport/BUCK @@ -9,14 +9,11 @@ rn_robolectric_test( "PUBLIC", ], deps = [ - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okio:okio"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), diff --git a/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java b/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java index 0c6bbde0e8bdd0..f155eebbddd312 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/devsupport/JSDebuggerWebSocketClientTest.java @@ -76,7 +76,7 @@ public void test_onMessage_WithInvalidContentType_ShouldNotTriggerCallbacks() th client.onMessage(null, ByteString.encodeUtf8("{\"replyID\":0, \"result\":\"OK\"}")); PowerMockito.verifyPrivate(client, never()) - .invoke("triggerRequestSuccess", anyInt(), anyString()); + .invoke("triggerRequestSuccess", anyInt(), nullable(String.class)); PowerMockito.verifyPrivate(client, never()).invoke("triggerRequestFailure", anyInt(), any()); } @@ -86,7 +86,7 @@ public void test_onMessage_WithoutReplyId_ShouldNotTriggerCallbacks() throws Exc client.onMessage(null, "{\"result\":\"OK\"}"); PowerMockito.verifyPrivate(client, never()) - .invoke("triggerRequestSuccess", anyInt(), anyString()); + .invoke("triggerRequestSuccess", anyInt(), nullable(String.class)); PowerMockito.verifyPrivate(client, never()).invoke("triggerRequestFailure", anyInt(), any()); } @@ -96,7 +96,7 @@ public void test_onMessage_With_Null_ReplyId_ShouldNotTriggerCallbacks() throws client.onMessage(null, "{\"replyID\":null, \"result\":\"OK\"}"); PowerMockito.verifyPrivate(client, never()) - .invoke("triggerRequestSuccess", anyInt(), anyString()); + .invoke("triggerRequestSuccess", anyInt(), nullable(String.class)); PowerMockito.verifyPrivate(client, never()).invoke("triggerRequestFailure", anyInt(), any()); } @@ -131,6 +131,7 @@ public void test_onMessage_With_Null_Error_ShouldTriggerRequestSuccess() throws JSDebuggerWebSocketClient client = PowerMockito.spy(new JSDebuggerWebSocketClient()); client.onMessage(null, "{\"replyID\":0, \"error\":null}"); - PowerMockito.verifyPrivate(client).invoke("triggerRequestSuccess", anyInt(), anyString()); + PowerMockito.verifyPrivate(client) + .invoke("triggerRequestSuccess", anyInt(), nullable(String.class)); } } diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK b/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK index 3b5337ebbb64f2..750600054fbf23 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/BUCK @@ -9,7 +9,6 @@ rn_robolectric_test( ], deps = [ YOGA_TARGET, - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/android/androidx:core"), react_native_dep("third-party/android/androidx:fragment"), @@ -18,10 +17,8 @@ rn_robolectric_test( react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okio:okio"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java index b46488d299d486..619b464d1d7c0b 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/dialog/DialogModuleTest.java @@ -25,7 +25,7 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.util.ActivityController; +import org.robolectric.android.controller.ActivityController; @RunWith(RobolectricTestRunner.class) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "androidx.*", "android.*"}) diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java index 36104c1e285e87..a6e401ed9b0c8c 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java @@ -518,9 +518,9 @@ public Object answer(InvocationOnMock invocation) throws Throwable { /* withCredentials */ false); // verify RequestBodyPart for image - PowerMockito.verifyStatic(times(1)); + PowerMockito.verifyStatic(RequestBodyUtil.class, times(1)); RequestBodyUtil.getFileInputStream(any(ReactContext.class), eq("imageUri")); - PowerMockito.verifyStatic(times(1)); + PowerMockito.verifyStatic(RequestBodyUtil.class, times(1)); RequestBodyUtil.create(MediaType.parse("image/jpg"), inputStream); // verify body @@ -579,7 +579,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { verify(mHttpClient, times(3)).newCall(any(Request.class)); mNetworkingModule.onCatalystInstanceDestroy(); - PowerMockito.verifyStatic(times(3)); + PowerMockito.verifyStatic(OkHttpCallUtil.class, times(3)); ArgumentCaptor clientArguments = ArgumentCaptor.forClass(OkHttpClient.class); ArgumentCaptor requestIdArguments = ArgumentCaptor.forClass(Integer.class); OkHttpCallUtil.cancelTag(clientArguments.capture(), requestIdArguments.capture()); @@ -624,7 +624,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { verify(mHttpClient, times(3)).newCall(any(Request.class)); mNetworkingModule.abortRequest(requests); - PowerMockito.verifyStatic(times(1)); + PowerMockito.verifyStatic(OkHttpCallUtil.class, times(1)); ArgumentCaptor clientArguments = ArgumentCaptor.forClass(OkHttpClient.class); ArgumentCaptor requestIdArguments = ArgumentCaptor.forClass(Integer.class); OkHttpCallUtil.cancelTag(clientArguments.capture(), requestIdArguments.capture()); @@ -635,7 +635,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { // If `cancelTag` would've been called again for the aborted call, we would have had // `requests + 1` calls. mNetworkingModule.onCatalystInstanceDestroy(); - PowerMockito.verifyStatic(times(requests)); + PowerMockito.verifyStatic(OkHttpCallUtil.class, times(requests)); clientArguments = ArgumentCaptor.forClass(OkHttpClient.class); requestIdArguments = ArgumentCaptor.forClass(Integer.class); OkHttpCallUtil.cancelTag(clientArguments.capture(), requestIdArguments.capture()); diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.java index 97f3a48171c891..ef21b78fe94019 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.java @@ -32,7 +32,8 @@ public class ReactCookieJarContainerTest { @Test public void testMissingJar() throws Exception { ReactCookieJarContainer jarContainer = mock(ReactCookieJarContainer.class); - assertThat(jarContainer.loadForRequest(any(HttpUrl.class)).size()).isEqualTo(0); + assertThat(jarContainer.loadForRequest(HttpUrl.parse("http://example.com")).size()) + .isEqualTo(0); } @Test @@ -40,7 +41,8 @@ public void testEmptyCookies() throws Exception { ReactCookieJarContainer jarContainer = mock(ReactCookieJarContainer.class); List cookies = new ArrayList<>(); when(jarContainer.loadForRequest(any(HttpUrl.class))).thenReturn(cookies); - assertThat(jarContainer.loadForRequest(any(HttpUrl.class)).size()).isEqualTo(0); + assertThat(jarContainer.loadForRequest(HttpUrl.parse("http://example.com")).size()) + .isEqualTo(0); } @Test @@ -51,7 +53,8 @@ public void testValidCookies() throws Exception { List cookies = new ArrayList<>(); cookies.add(new Cookie.Builder().name("valid").value("valid value").domain("domain").build()); when(cookieJar.loadForRequest(any(HttpUrl.class))).thenReturn(cookies); - assertThat(jarContainer.loadForRequest(any(HttpUrl.class)).size()).isEqualTo(1); + assertThat(jarContainer.loadForRequest(HttpUrl.parse("http://example.com")).size()) + .isEqualTo(1); } @Test @@ -62,6 +65,7 @@ public void testInvalidCookies() throws Exception { List cookies = new ArrayList<>(); cookies.add(new Cookie.Builder().name("valid").value("înválíd välūė").domain("domain").build()); when(cookieJar.loadForRequest(any(HttpUrl.class))).thenReturn(cookies); - assertThat(jarContainer.loadForRequest(any(HttpUrl.class)).size()).isEqualTo(0); + assertThat(jarContainer.loadForRequest(HttpUrl.parse("http://example.com")).size()) + .isEqualTo(0); } } diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/share/ShareModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/share/ShareModuleTest.java index 75a2135c223e31..2260bc47dde664 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/share/ShareModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/share/ShareModuleTest.java @@ -9,6 +9,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.robolectric.Shadows.shadowOf; import android.app.Activity; import android.content.Intent; @@ -17,6 +18,7 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.JavaOnlyMap; import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactTestHelper; import com.facebook.react.bridge.WritableMap; import org.junit.After; @@ -31,14 +33,23 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.internal.ShadowExtractor; -import org.robolectric.shadows.ShadowApplication; @PrepareForTest({Arguments.class}) @RunWith(RobolectricTestRunner.class) -@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "androidx.*", "android.*"}) +@PowerMockIgnore({ + "org.mockito.*", + "org.robolectric.*", + "androidx.*", + "android.*", + "javax.xml.*", + "org.xml.sax.*", + "org.w3c.dom.*", + "org.springframework.context.*", + "org.apache.log4j.*" +}) public class ShareModuleTest { private Activity mActivity; @@ -58,7 +69,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } }); - mShareModule = new ShareModule(ReactTestHelper.createCatalystContextForTest()); + mActivity = Robolectric.setupActivity(Activity.class); + + ReactApplicationContext applicationContext = ReactTestHelper.createCatalystContextForTest(); + applicationContext.onNewIntent(mActivity, new Intent()); + mShareModule = new ShareModule(applicationContext); } @After @@ -81,9 +96,7 @@ public void testShareDialog() { mShareModule.share(content, dialogTitle, promise); - final Intent chooserIntent = - ((ShadowApplication) ShadowExtractor.extract(RuntimeEnvironment.application)) - .getNextStartedActivity(); + final Intent chooserIntent = shadowOf(RuntimeEnvironment.application).getNextStartedActivity(); assertNotNull("Dialog was not displayed", chooserIntent); assertEquals(Intent.ACTION_CHOOSER, chooserIntent.getAction()); assertEquals(dialogTitle, chooserIntent.getExtras().get(Intent.EXTRA_TITLE)); diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/storage/AsyncStorageModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/storage/AsyncStorageModuleTest.java index 45b3f1b0f36d1b..bc5d9bc332649d 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/storage/AsyncStorageModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/storage/AsyncStorageModuleTest.java @@ -36,7 +36,7 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.util.concurrent.RoboExecutorService; +import org.robolectric.android.util.concurrent.RoboExecutorService; /** Tests for {@link com.facebook.react.modules.storage.AsyncStorageModule}. */ @PrepareForTest({Arguments.class}) diff --git a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/BUCK b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/BUCK index 18dec467c72f2c..f34e2c12e0e360 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/packagerconnection/BUCK @@ -9,14 +9,11 @@ rn_robolectric_test( "PUBLIC", ], deps = [ - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okio:okio"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react/packagerconnection:packagerconnection"), ], ) diff --git a/ReactAndroid/src/test/java/com/facebook/react/uimanager/BUCK b/ReactAndroid/src/test/java/com/facebook/react/uimanager/BUCK index ad592a0dddf576..bc96fa72a28aa6 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/uimanager/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/uimanager/BUCK @@ -17,7 +17,6 @@ rn_robolectric_test( ], deps = [ YOGA_TARGET, - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/android/androidx:core"), react_native_dep("third-party/android/androidx:fragment"), @@ -26,10 +25,8 @@ rn_robolectric_test( react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okio:okio"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), diff --git a/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/BUCK b/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/BUCK index 6c791141d5435f..3124e8db34698e 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/BUCK @@ -11,7 +11,6 @@ rn_robolectric_test( YOGA_TARGET, react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react/uimanager:uimanager"), ], ) diff --git a/ReactAndroid/src/test/java/com/facebook/react/util/BUCK b/ReactAndroid/src/test/java/com/facebook/react/util/BUCK index 12a2a521fc5be3..853b0a8a2f90bb 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/util/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/util/BUCK @@ -7,12 +7,9 @@ rn_robolectric_test( "PUBLIC", ], deps = [ - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react/util:util"), react_native_target("java/com/facebook/react/bridge:bridge"), ], diff --git a/ReactAndroid/src/test/java/com/facebook/react/views/BUCK b/ReactAndroid/src/test/java/com/facebook/react/views/BUCK index 5b616f6630bdc9..058829726fbfa4 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/views/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/views/BUCK @@ -9,7 +9,6 @@ rn_robolectric_test( contacts = ["oncall+fbandroid_sheriff@xmail.facebook.com"], deps = [ YOGA_TARGET, - react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock"), react_native_dep("libraries/fresco/fresco-react-native:fresco-drawee"), react_native_dep("libraries/fresco/fresco-react-native:fresco-react-native"), react_native_dep("libraries/fresco/fresco-react-native:imagepipeline"), @@ -22,10 +21,8 @@ rn_robolectric_test( react_native_dep("third-party/java/fest:fest"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/junit:junit"), - react_native_dep("third-party/java/mockito:mockito"), react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okio:okio"), - react_native_dep("third-party/java/robolectric3/robolectric:robolectric"), react_native_target("java/com/facebook/react:react"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), diff --git a/ReactAndroid/src/test/java/org/mockito/configuration/BUCK b/ReactAndroid/src/test/java/org/mockito/configuration/BUCK index 99bce2a385eb49..a29174b85a037e 100644 --- a/ReactAndroid/src/test/java/org/mockito/configuration/BUCK +++ b/ReactAndroid/src/test/java/org/mockito/configuration/BUCK @@ -1,4 +1,4 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_android_toplevel_dep", "rn_android_library") rn_android_library( name = "configuration", @@ -7,6 +7,6 @@ rn_android_library( "PUBLIC", ], deps = [ - react_native_dep("third-party/java/mockito:mockito"), + react_native_android_toplevel_dep("third-party/java/mockito2:mockito2"), ], ) diff --git a/ReactCommon/ReactCommon.podspec b/ReactCommon/ReactCommon.podspec index 82b42b6c0a2280..ce62a7cfb9a539 100644 --- a/ReactCommon/ReactCommon.podspec +++ b/ReactCommon/ReactCommon.podspec @@ -38,6 +38,7 @@ Pod::Spec.new do |s| s.subspec "turbomodule" do |ss| ss.dependency "React-callinvoker", version + ss.dependency "React-perflogger", version ss.dependency "React-Core", version ss.dependency "React-cxxreact", version ss.dependency "React-jsi", version diff --git a/ReactCommon/better/BUCK b/ReactCommon/better/BUCK index fb403d3a8dfad5..53358d1a55679f 100644 --- a/ReactCommon/better/BUCK +++ b/ReactCommon/better/BUCK @@ -35,11 +35,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/callinvoker/BUCK b/ReactCommon/callinvoker/BUCK index 0f702cb7282867..b050d1f025424a 100644 --- a/ReactCommon/callinvoker/BUCK +++ b/ReactCommon/callinvoker/BUCK @@ -16,8 +16,7 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID, APPLE), preferred_linkage = "static", preprocessor_flags = [ diff --git a/ReactCommon/config/BUCK b/ReactCommon/config/BUCK index 9c81d30e98dab6..9640ee45c1a529 100644 --- a/ReactCommon/config/BUCK +++ b/ReactCommon/config/BUCK @@ -24,11 +24,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/cxxreact/Android.mk b/ReactCommon/cxxreact/Android.mk index df9b9e640bc69c..a5024364c6088d 100644 --- a/ReactCommon/cxxreact/Android.mk +++ b/ReactCommon/cxxreact/Android.mk @@ -19,7 +19,7 @@ LOCAL_CFLAGS := \ LOCAL_CFLAGS += -fexceptions -frtti -Wno-unused-lambda-capture -LOCAL_STATIC_LIBRARIES := boost jsi callinvoker runtimeexecutor +LOCAL_STATIC_LIBRARIES := boost jsi callinvoker reactperflogger runtimeexecutor LOCAL_SHARED_LIBRARIES := jsinspector libfolly_json glog include $(BUILD_STATIC_LIBRARY) @@ -27,6 +27,7 @@ include $(BUILD_STATIC_LIBRARY) $(call import-module,fb) $(call import-module,folly) $(call import-module,callinvoker) +$(call import-module,reactperflogger) $(call import-module,jsc) $(call import-module,glog) $(call import-module,jsi) diff --git a/ReactCommon/cxxreact/BUCK b/ReactCommon/cxxreact/BUCK index d5453556bd6e91..36d242b889c7d2 100644 --- a/ReactCommon/cxxreact/BUCK +++ b/ReactCommon/cxxreact/BUCK @@ -18,10 +18,9 @@ rn_xplat_cxx_library( prefix = "cxxreact", ), compiler_flags = CXX_LIBRARY_COMPILER_FLAGS, - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = get_apple_compiler_flags(), - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], @@ -44,10 +43,9 @@ rn_xplat_cxx_library( "-fexceptions", "-frtti", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = get_apple_compiler_flags(), - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], preprocessor_flags = [ "-DWITH_FBREMAP=1", ], @@ -71,6 +69,7 @@ rn_xplat_cxx_library( "-fexceptions", ], fbobjc_compiler_flags = get_apple_compiler_flags(), + labels = ["supermodule:xplat/default/public.react_native.infra"], soname = "libxplat_react_module_samplemodule.$(ext)", visibility = [ "PUBLIC", @@ -128,12 +127,11 @@ rn_xplat_cxx_library( "-fexceptions", "-frtti", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_preprocessor_flags = get_android_inspector_flags(), fbobjc_compiler_flags = get_apple_compiler_flags(), fbobjc_force_static = True, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE), preprocessor_flags = [ @@ -155,6 +153,7 @@ rn_xplat_cxx_library( react_native_xplat_target("jsinspector:jsinspector"), react_native_xplat_target("microprofiler:microprofiler"), react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + react_native_xplat_target("reactperflogger:reactperflogger"), "//third-party/glog:glog", "//xplat/folly:optional", ], diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index 94ab82bfe0bdad..bfbb608553b525 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -58,6 +58,18 @@ std::string CxxNativeModule::getName() { return name_; } +std::string CxxNativeModule::getSyncMethodName(unsigned int reactMethodId) { + if (reactMethodId >= methods_.size()) { + throw std::invalid_argument(folly::to( + "methodId ", + reactMethodId, + " out of range [0..", + methods_.size(), + "]")); + } + return methods_[reactMethodId].name; +} + std::vector CxxNativeModule::getMethods() { lazyInit(); diff --git a/ReactCommon/cxxreact/CxxNativeModule.h b/ReactCommon/cxxreact/CxxNativeModule.h index 2c2230409d1331..9d730ff7942470 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.h +++ b/ReactCommon/cxxreact/CxxNativeModule.h @@ -37,6 +37,7 @@ class RN_EXPORT CxxNativeModule : public NativeModule { messageQueueThread_(messageQueueThread) {} std::string getName() override; + std::string getSyncMethodName(unsigned int methodId) override; std::vector getMethods() override; folly::dynamic getConstants() override; void invoke(unsigned int reactMethodId, folly::dynamic &¶ms, int callId) diff --git a/ReactCommon/cxxreact/ModuleRegistry.cpp b/ReactCommon/cxxreact/ModuleRegistry.cpp index b59dac82f44f5c..da75bc5b5857f3 100644 --- a/ReactCommon/cxxreact/ModuleRegistry.cpp +++ b/ReactCommon/cxxreact/ModuleRegistry.cpp @@ -8,6 +8,7 @@ #include "ModuleRegistry.h" #include +#include #include "NativeModule.h" #include "SystraceSection.h" @@ -99,14 +100,37 @@ folly::Optional ModuleRegistry::getConfig( if (it == modulesByName_.end()) { if (unknownModules_.find(name) != unknownModules_.end()) { + BridgeNativeModulePerfLogger::moduleJSRequireBeginningFail(name.c_str()); + BridgeNativeModulePerfLogger::moduleJSRequireEndingStart(name.c_str()); return folly::none; } - if (!moduleNotFoundCallback_ || !moduleNotFoundCallback_(name) || - (it = modulesByName_.find(name)) == modulesByName_.end()) { + + if (!moduleNotFoundCallback_) { unknownModules_.insert(name); + BridgeNativeModulePerfLogger::moduleJSRequireBeginningFail(name.c_str()); + BridgeNativeModulePerfLogger::moduleJSRequireEndingStart(name.c_str()); return folly::none; } + + BridgeNativeModulePerfLogger::moduleJSRequireBeginningEnd(name.c_str()); + + bool wasModuleLazilyLoaded = moduleNotFoundCallback_(name); + it = modulesByName_.find(name); + + bool wasModuleRegisteredWithRegistry = + wasModuleLazilyLoaded && it != modulesByName_.end(); + + if (!wasModuleRegisteredWithRegistry) { + BridgeNativeModulePerfLogger::moduleJSRequireEndingStart(name.c_str()); + unknownModules_.insert(name); + return folly::none; + } + } else { + BridgeNativeModulePerfLogger::moduleJSRequireBeginningEnd(name.c_str()); } + + // If we've gotten this far, then we've signaled moduleJSRequireBeginningEnd + size_t index = it->second; CHECK(index < modules_.size()); @@ -118,6 +142,12 @@ folly::Optional ModuleRegistry::getConfig( { SystraceSection s_("ModuleRegistry::getConstants", "module", name); + /** + * In the case that there are constants, we'll initialize the NativeModule, + * and signal moduleJSRequireEndingStart. Otherwise, we'll simply signal the + * event. The Module will be initialized when we invoke one of its + * NativeModule methods. + */ config.push_back(module->getConstants()); } @@ -158,6 +188,26 @@ folly::Optional ModuleRegistry::getConfig( } } +std::string ModuleRegistry::getModuleName(unsigned int moduleId) { + if (moduleId >= modules_.size()) { + throw std::runtime_error(folly::to( + "moduleId ", moduleId, " out of range [0..", modules_.size(), ")")); + } + + return modules_[moduleId]->getName(); +} + +std::string ModuleRegistry::getModuleSyncMethodName( + unsigned int moduleId, + unsigned int methodId) { + if (moduleId >= modules_.size()) { + throw std::runtime_error(folly::to( + "moduleId ", moduleId, " out of range [0..", modules_.size(), ")")); + } + + return modules_[moduleId]->getSyncMethodName(methodId); +} + void ModuleRegistry::callNativeMethod( unsigned int moduleId, unsigned int methodId, diff --git a/ReactCommon/cxxreact/ModuleRegistry.h b/ReactCommon/cxxreact/ModuleRegistry.h index b24f5390e30f46..fba4496a621734 100644 --- a/ReactCommon/cxxreact/ModuleRegistry.h +++ b/ReactCommon/cxxreact/ModuleRegistry.h @@ -59,6 +59,11 @@ class RN_EXPORT ModuleRegistry { unsigned int methodId, folly::dynamic &&args); + std::string getModuleName(unsigned int moduleId); + std::string getModuleSyncMethodName( + unsigned int moduleId, + unsigned int methodName); + private: // This is always populated std::vector> modules_; diff --git a/ReactCommon/cxxreact/NativeModule.h b/ReactCommon/cxxreact/NativeModule.h index 6e7287cf469c88..2441953b4a040b 100644 --- a/ReactCommon/cxxreact/NativeModule.h +++ b/ReactCommon/cxxreact/NativeModule.h @@ -31,6 +31,7 @@ class NativeModule { public: virtual ~NativeModule() {} virtual std::string getName() = 0; + virtual std::string getSyncMethodName(unsigned int methodId) = 0; virtual std::vector getMethods() = 0; virtual folly::dynamic getConstants() = 0; virtual void diff --git a/ReactCommon/cxxreact/NativeToJsBridge.cpp b/ReactCommon/cxxreact/NativeToJsBridge.cpp index 64b0673423b8d9..45651ac12d0bad 100644 --- a/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "Instance.h" #include "JSBigString.h" @@ -55,10 +56,14 @@ class JsToNativeBridge : public react::ExecutorDelegate { m_batchHadNativeModuleOrTurboModuleCalls = m_batchHadNativeModuleOrTurboModuleCalls || !calls.empty(); + std::vector methodCalls = parseMethodCalls(std::move(calls)); + BridgeNativeModulePerfLogger::asyncMethodCallBatchPreprocessEnd( + (int)methodCalls.size()); + // An exception anywhere in here stops processing of the batch. This // was the behavior of the Android bridge, and since exception handling // terminates the whole bridge, there's not much point in continuing. - for (auto &call : parseMethodCalls(std::move(calls))) { + for (auto &call : methodCalls) { m_registry->callNativeMethod( call.moduleId, call.methodId, std::move(call.arguments), call.callId); } diff --git a/ReactCommon/cxxreact/React-cxxreact.podspec b/ReactCommon/cxxreact/React-cxxreact.podspec index cdb34aa745a8ef..bea5ad31d35596 100644 --- a/ReactCommon/cxxreact/React-cxxreact.podspec +++ b/ReactCommon/cxxreact/React-cxxreact.podspec @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright (c) Facebook, Inc. and its affiliates. # # This source code is licensed under the MIT license found in the @@ -42,4 +43,5 @@ Pod::Spec.new do |s| s.dependency "React-jsinspector", version s.dependency "React-callinvoker", version s.dependency "React-runtimeexecutor", version + s.dependency "React-perflogger", version end diff --git a/ReactCommon/fabric/animations/BUCK b/ReactCommon/fabric/animations/BUCK new file mode 100644 index 00000000000000..123fb6c45e2386 --- /dev/null +++ b/ReactCommon/fabric/animations/BUCK @@ -0,0 +1,96 @@ +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", + "fb_xplat_cxx_test", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "animations", + srcs = glob( + ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), + ), + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/animations", + ), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++14", + "-Wall", + "-lm", + ], + 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", + "//xplat/jsi:JSIDynamic", + "//xplat/jsi:jsi", + react_native_xplat_target("config:config"), + react_native_xplat_target("fabric/componentregistry:componentregistry"), + react_native_xplat_target("fabric/components/view:view"), + react_native_xplat_target("fabric/core:core"), + react_native_xplat_target("fabric/debug:debug"), + react_native_xplat_target("fabric/mounting:mounting"), + react_native_xplat_target("fabric/uimanager:uimanager"), + react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + ], +) + +fb_xplat_cxx_test( + name = "tests", + srcs = glob(["tests/**/*.cpp"]), + headers = glob(["tests/**/*.h"]), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++14", + "-Wall", + ], + contacts = ["oncall+react_native@xmail.facebook.com"], + platforms = (ANDROID, APPLE, CXX), + deps = [ + ":animations", + "//xplat/folly:molly", + "//xplat/third-party/gmock:gtest", + react_native_xplat_target("config:config"), + react_native_xplat_target("fabric/components/activityindicator:activityindicator"), + react_native_xplat_target("fabric/components/image:image"), + react_native_xplat_target("fabric/components/root:root"), + react_native_xplat_target("fabric/components/scrollview:scrollview"), + react_native_xplat_target("fabric/components/view:view"), + "//xplat/js/react-native-github:generated_components-rncore", + ], +) diff --git a/ReactCommon/fabric/animations/LayoutAnimationDriver.cpp b/ReactCommon/fabric/animations/LayoutAnimationDriver.cpp new file mode 100644 index 00000000000000..a1e1da95644aef --- /dev/null +++ b/ReactCommon/fabric/animations/LayoutAnimationDriver.cpp @@ -0,0 +1,197 @@ +/* + * 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 "LayoutAnimationDriver.h" + +#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, + uint64_t now) const { + for (auto &animation : inflightAnimations_) { + if (animation.surfaceId != surfaceId) { + continue; + } + + int incompleteAnimations = 0; + for (const auto &keyframe : animation.keyFrames) { + if (keyframe.type == AnimationConfigurationType::Noop) { + continue; + } + + auto const &baselineShadowView = keyframe.viewStart; + auto const &finalShadowView = keyframe.viewEnd; + + // 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 = + (keyframe.type == AnimationConfigurationType::Delete + ? layoutAnimationConfig.deleteConfig + : (keyframe.type == AnimationConfigurationType::Create + ? layoutAnimationConfig.createConfig + : layoutAnimationConfig.updateConfig)); + + // Interpolate + std::pair progress = + calculateAnimationProgress(now, animation, *mutationConfig); + double animationTimeProgressLinear = progress.first; + double animationInterpolationFactor = progress.second; + + auto mutatedShadowView = createInterpolatedShadowView( + animationInterpolationFactor, + *mutationConfig, + baselineShadowView, + finalShadowView); + + // Create the mutation instruction + mutationsList.push_back(ShadowViewMutation::UpdateMutation( + keyframe.parentView, baselineShadowView, mutatedShadowView, -1)); + + if (animationTimeProgressLinear < 1) { + incompleteAnimations++; + } + } + + // Are there no ongoing mutations left in this animation? + if (incompleteAnimations == 0) { + animation.completed = true; + } + } + + // Clear out finished animations + for (auto it = inflightAnimations_.begin(); + it != inflightAnimations_.end();) { + const auto &animation = *it; + if (animation.completed) { + // Queue up "final" mutations for all keyframes in the completed animation + for (auto const &keyframe : animation.keyFrames) { + if (keyframe.finalMutationForKeyFrame.hasValue()) { + mutationsList.push_back(*keyframe.finalMutationForKeyFrame); + } + } + + it = inflightAnimations_.erase(it); + } else { + it++; + } + } +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/animations/LayoutAnimationDriver.h b/ReactCommon/fabric/animations/LayoutAnimationDriver.h new file mode 100644 index 00000000000000..3aa04f99163bca --- /dev/null +++ b/ReactCommon/fabric/animations/LayoutAnimationDriver.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 +#include +#include +#include +#include +#include + +#include + +#include "LayoutAnimationKeyFrameManager.h" + +namespace facebook { +namespace react { + +class LayoutAnimationDriver : public LayoutAnimationKeyFrameManager { + public: + LayoutAnimationDriver(LayoutAnimationStatusDelegate *delegate) + : LayoutAnimationKeyFrameManager(delegate) {} + + virtual ~LayoutAnimationDriver() {} + + 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 +} // namespace facebook diff --git a/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp new file mode 100644 index 00000000000000..91b73f5bdaea05 --- /dev/null +++ b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.cpp @@ -0,0 +1,944 @@ +/* + * 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 "LayoutAnimationKeyFrameManager.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace facebook { +namespace react { + +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); + } + + 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); + } + + 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()) { + return {}; + } + auto const animationTypeParam = typeIt->second; + if (animationTypeParam.empty() || !animationTypeParam.isString()) { + return {}; + } + const auto animationType = parseAnimationType(animationTypeParam.asString()); + if (!animationType) { + return {}; + } + + AnimationProperty animationProperty = AnimationProperty::NotApplicable; + if (parsePropertyType) { + auto const propertyIt = config.find("property"); + if (propertyIt == config.items().end()) { + return {}; + } + auto const animationPropertyParam = propertyIt->second; + if (animationPropertyParam.empty() || !animationPropertyParam.isString()) { + return {}; + } + const auto animationPropertyParsed = + parseAnimationProperty(animationPropertyParam.asString()); + if (!animationPropertyParsed) { + 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 { + return {}; + } + } + + auto const delayIt = config.find("delay"); + if (delayIt != config.items().end()) { + if (delayIt->second.isDouble()) { + delay = delayIt->second.asDouble(); + } else { + return {}; + } + } + + auto const springDampingIt = config.find("springDamping"); + if (springDampingIt != config.items().end() && + springDampingIt->second.isDouble()) { + if (springDampingIt->second.isDouble()) { + springDamping = springDampingIt->second.asDouble(); + } else { + return {}; + } + } + + auto const initialVelocityIt = config.find("initialVelocity"); + if (initialVelocityIt != config.items().end()) { + if (initialVelocityIt->second.isDouble()) { + initialVelocity = initialVelocityIt->second.asDouble(); + } else { + return {}; + } + } + + return better::optional(AnimationConfig{*animationType, + animationProperty, + duration, + delay, + springDamping, + initialVelocity}); +} + +// Parse animation config from JS +static better::optional parseLayoutAnimationConfig( + folly::dynamic const &config) { + if (config.empty() || !config.isObject()) { + return {}; + } + + auto const durationIt = config.find("duration"); + if (durationIt == config.items().end() || !durationIt->second.isDouble()) { + return {}; + } + const double duration = durationIt->second.asDouble(); + + const auto createConfig = + parseAnimationConfig(config["create"], duration, true); + if (!createConfig) { + return {}; + } + + const auto updateConfig = + parseAnimationConfig(config["update"], duration, false); + if (!updateConfig) { + return {}; + } + + const auto deleteConfig = + parseAnimationConfig(config["delete"], duration, true); + if (!deleteConfig) { + return {}; + } + + return better::optional(LayoutAnimationConfig{ + duration, *createConfig, *updateConfig, *deleteConfig}); +} + +/** + * Globally configure next LayoutAnimation. + */ +void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation( + RawValue const &config, + std::shared_ptr successCallback, + std::shared_ptr errorCallback) const { + auto layoutAnimationConfig = + parseLayoutAnimationConfig((folly::dynamic)config); + + if (layoutAnimationConfig) { + std::lock_guard lock(currentAnimationMutex_); + currentAnimation_ = better::optional{ + LayoutAnimation{-1, + 0, + false, + *layoutAnimationConfig, + successCallback, + errorCallback, + {}}}; + } else { + // TODO: call errorCallback + LOG(ERROR) << "Parsing LayoutAnimationConfig failed: " + << (folly::dynamic)config; + } +} + +void LayoutAnimationKeyFrameManager::setLayoutAnimationStatusDelegate( + LayoutAnimationStatusDelegate *delegate) const { + std::lock_guard lock(layoutAnimationStatusDelegateMutex_); + layoutAnimationStatusDelegate_ = delegate; +} + +bool LayoutAnimationKeyFrameManager::shouldOverridePullTransaction() const { + return shouldAnimateFrame(); +} + +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(); +} + +static inline const float +interpolateFloats(float coefficient, float oldValue, float newValue) { + return oldValue + (newValue - oldValue) * coefficient; +} + +std::pair +LayoutAnimationKeyFrameManager::calculateAnimationProgress( + uint64_t now, + const LayoutAnimation &animation, + const AnimationConfig &mutationConfig) const { + 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}; + } + + 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}; + } +} + +void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( + SurfaceId surfaceId, + ShadowViewMutation const &mutation) const { + bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; + bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; + assert(isRemoveMutation || isInsertMutation); + + if (mutatedViewIsVirtual(mutation)) { + return; + } + + for (auto &inflightAnimation : inflightAnimations_) { + if (inflightAnimation.surfaceId != surfaceId) { + continue; + } + + for (auto it = inflightAnimation.keyFrames.begin(); + it != inflightAnimation.keyFrames.end(); + it++) { + auto &animatedKeyFrame = *it; + + // 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.type != AnimationConfigurationType::Noop) { + continue; + } + if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { + continue; + } + ShadowViewMutation &finalAnimationMutation = + *animatedKeyFrame.finalMutationForKeyFrame; + + if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) { + continue; + } + + // Do we need to adjust the index of this operation? + if (isRemoveMutation && mutation.index <= finalAnimationMutation.index) { + finalAnimationMutation.index--; + } else if ( + isInsertMutation && mutation.index <= finalAnimationMutation.index) { + finalAnimationMutation.index++; + } + } + } +} + +better::optional +LayoutAnimationKeyFrameManager::pullTransaction( + SurfaceId surfaceId, + MountingTransaction::Number transactionNumber, + MountingTelemetry 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(); + + 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 + + // 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{}; + for (auto &mutation : mutations) { + auto const &baselineShadowView = + (mutation.type == ShadowViewMutation::Type::Insert) + ? mutation.newChildShadowView + : mutation.oldChildShadowView; + + for (auto &inflightAnimation : inflightAnimations_) { + if (inflightAnimation.surfaceId != surfaceId) { + continue; + } + + for (auto it = inflightAnimation.keyFrames.begin(); + it != inflightAnimation.keyFrames.end();) { + auto &animatedKeyFrame = *it; + + // Conflicting animation detected + if (animatedKeyFrame.tag == baselineShadowView.tag) { + auto const layoutAnimationConfig = + inflightAnimation.layoutAnimationConfig; + + auto const mutationConfig = + (animatedKeyFrame.type == AnimationConfigurationType::Delete + ? layoutAnimationConfig.deleteConfig + : (animatedKeyFrame.type == + AnimationConfigurationType::Create + ? layoutAnimationConfig.createConfig + : layoutAnimationConfig.updateConfig)); + + conflictingAnimations.push_back(std::make_tuple( + animatedKeyFrame, *mutationConfig, &inflightAnimation)); + + // Delete from existing animation + it = inflightAnimation.keyFrames.erase(it); + } else { + it++; + } + } + } + } + + // Are we animating this list of mutations? + better::optional currentAnimation{}; + { + std::lock_guard lock(currentAnimationMutex_); + if (currentAnimation_) { + currentAnimation = currentAnimation_; + currentAnimation_ = {}; + } + } + + if (currentAnimation) { + LayoutAnimation animation = currentAnimation.value(); + 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 createdTags; + std::unordered_map movedTags; + std::vector reparentedTags; + for (const auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Insert) { + insertedTags.push_back(mutation.newChildShadowView.tag); + } + if (mutation.type == ShadowViewMutation::Type::Create) { + createdTags.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); + auto const &componentDescriptor = + getComponentDescriptorForShadowView(baselineShadowView); + + 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 = + (mutation.type == ShadowViewMutation::Delete && + std::find( + createdTags.begin(), + createdTags.end(), + mutation.oldChildShadowView.tag) != createdTags.end()) || + (mutation.type == ShadowViewMutation::Create && + std::find( + reparentedTags.begin(), + reparentedTags.end(), + mutation.newChildShadowView.tag) != reparentedTags.end()); + + if (isRemoveReinserted) { + movedTags.insert({mutation.oldChildShadowView.tag, mutation}); + } + + if (isReparented && mutation.type == ShadowViewMutation::Delete) { + reparentedTags.push_back(mutation.oldChildShadowView.tag); + } + + // 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(); + 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 + // generate an Update mutation for Remove+Insert pairs to animate + // the layout. + // The corresponding Remove and Insert instructions will instead + // be treated as "immediate" instructions. + auto 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}); + } + } + + // 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) { + immediateMutations.push_back(mutation); + + // Adjust indices for any non-directly-conflicting animations that + // affect the same parent view by inserting or removing anything + // from the hierarchy. + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } + } + + // Deletes, non-move inserts, updates get animated + if (!wasInsertedTagRemoved && !isRemoveReinserted && !isReparented && + haveConfiguration && + mutation.type != ShadowViewMutation::Type::Create) { + ShadowView viewStart = ShadowView( + mutation.type == ShadowViewMutation::Type::Insert + ? mutation.newChildShadowView + : mutation.oldChildShadowView); + ShadowView viewFinal = ShadowView( + mutation.type == ShadowViewMutation::Type::Update + ? mutation.newChildShadowView + : viewStart); + ShadowView parent = mutation.parentShadowView; + Tag tag = viewStart.tag; + Tag parentTag = mutation.parentShadowView.tag; + + AnimationKeyFrame keyFrame{}; + if (mutation.type == ShadowViewMutation::Type::Insert) { + if (mutationConfig->animationProperty == + AnimationProperty::Opacity) { + auto props = componentDescriptor.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) { + auto props = componentDescriptor.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); + } + viewStart.props = props; + } + + keyFrame = AnimationKeyFrame{{}, + AnimationConfigurationType::Create, + tag, + parent, + viewStart, + viewFinal, + 0}; + } else if (mutation.type == ShadowViewMutation::Type::Delete) { + if (mutationConfig->animationProperty == + AnimationProperty::Opacity) { + auto props = componentDescriptor.cloneProps(viewFinal.props, {}); + const auto viewProps = + dynamic_cast(props.get()); + if (viewProps != nullptr) { + const_cast(viewProps)->opacity = 0; + } + 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) { + auto props = componentDescriptor.cloneProps(viewFinal.props, {}); + const auto viewProps = + dynamic_cast(props.get()); + if (viewProps != nullptr) { + const_cast(viewProps)->transform = + Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1); + } + viewFinal.props = props; + } + + keyFrame = AnimationKeyFrame{ + better::optional(mutation), + AnimationConfigurationType::Delete, + tag, + parent, + viewStart, + viewFinal, + 0}; + } else if (mutation.type == ShadowViewMutation::Type::Update) { + viewFinal = ShadowView(mutation.newChildShadowView); + + keyFrame = AnimationKeyFrame{ + better::optional(mutation), + AnimationConfigurationType::Update, + tag, + parent, + viewStart, + viewFinal, + 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); + + // For remove instructions: since the execution of the Remove + // instruction will be delayed and therefore may execute outside of + // otherwise-expected order, other views may be inserted before the + // Remove is executed, requiring index adjustment. + { + int adjustedIndex = mutation.index; + for (const auto &otherMutation : mutations) { + if (otherMutation.type == ShadowViewMutation::Type::Insert && + otherMutation.parentShadowView.tag == parentTag) { + if (otherMutation.index <= adjustedIndex && + !mutatedViewIsVirtual(otherMutation)) { + adjustedIndex++; + } + } + } + + mutation = ShadowViewMutation::RemoveMutation( + mutation.parentShadowView, + mutation.oldChildShadowView, + adjustedIndex); + } + + keyFrame = AnimationKeyFrame{ + better::optional(mutation), + AnimationConfigurationType::Noop, + tag, + parent, + {}, + {}, + 0}; + } + + // Handle conflicting animations + for (auto &conflictingKeyframeTuple : conflictingAnimations) { + auto &conflictingKeyFrame = std::get<0>(conflictingKeyframeTuple); + 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, + std::get<1>(conflictingKeyframeTuple), + conflictingKeyFrame.viewStart, + conflictingKeyFrame.viewEnd); + + // 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); + + // We're guaranteed that a tag only has one animation associated + // with it, so we can break here. If we support multiple + // animations and animation curves over the same tag in the + // future, this will need to be modified to support that. + break; + } + } + + keyFramesToAnimate.push_back(keyFrame); + } + } + +#ifdef RN_SHADOW_TREE_INTROSPECTION + { + std::stringstream ss(getDebugDescription(immediateMutations, {})); + std::string to; + while (std::getline(ss, to, '\n')) { + LOG(ERROR) + << "LayoutAnimationKeyFrameManager.cpp: got IMMEDIATE list: Line: " + << to; + } + } + + { + std::stringstream ss(getDebugDescription(mutationsToAnimate, {})); + std::string to; + while (std::getline(ss, to, '\n')) { + LOG(ERROR) + << "LayoutAnimationKeyFrameManager.cpp: got FINAL list: Line: " + << to; + } + } +#endif + + animation.keyFrames = keyFramesToAnimate; + inflightAnimations_.push_back(animation); + + // These will be executed immediately. + mutations = immediateMutations; + } /* if (currentAnimation) */ else { + // If there's no "next" animation, make sure we queue up "final" + // operations from all ongoing animations. + ShadowViewMutationList finalMutationsForConflictingAnimations{}; + for (auto &conflictingKeyframeTuple : conflictingAnimations) { + auto &keyFrame = std::get<0>(conflictingKeyframeTuple); + if (keyFrame.finalMutationForKeyFrame.hasValue()) { + finalMutationsForConflictingAnimations.push_back( + *keyFrame.finalMutationForKeyFrame); + } + } + + // Append mutations to this list and swap - so that the final + // conflicting mutations happen before any other mutations + finalMutationsForConflictingAnimations.insert( + finalMutationsForConflictingAnimations.end(), + mutations.begin(), + mutations.end()); + mutations = finalMutationsForConflictingAnimations; + + // Adjust pending mutation indices base on these operations + for (auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } + } + } + } // if (mutations) + + // We never commit a different root or modify anything - + // we just send additional mutations to the mounting layer until the + // animations are finished and the mounting layer (view) represents exactly + // what is in the most recent shadow tree + // Add animation mutations to the end of our existing mutations list in this + // function. + ShadowViewMutationList mutationsForAnimation{}; + animationMutationsForFrame(surfaceId, mutationsForAnimation, now); + + // Adjust pending mutation indices base on these operations + // For example: if a final "remove" mutation has been performed, and there is + // another that has not yet been executed because it is a part of an ongoing + // animation, its index may need to be adjusted. + for (auto const &animatedMutation : mutationsForAnimation) { + if (animatedMutation.type == ShadowViewMutation::Type::Insert || + animatedMutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, animatedMutation); + } + } + + mutations.insert( + mutations.end(), + mutationsForAnimation.begin(), + mutationsForAnimation.end()); + + // 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(); + } + } + + // TODO: fill in telemetry + return MountingTransaction{ + surfaceId, transactionNumber, std::move(mutations), {}}; +} + +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; +} + +ComponentDescriptor const & +LayoutAnimationKeyFrameManager::getComponentDescriptorForShadowView( + ShadowView const &shadowView) const { + return componentDescriptorRegistry_->at(shadowView.componentHandle); +} + +void LayoutAnimationKeyFrameManager::setComponentDescriptorRegistry( + const SharedComponentDescriptorRegistry &componentDescriptorRegistry) { + componentDescriptorRegistry_ = componentDescriptorRegistry; +} + +/** + * 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, + AnimationConfig const &animationConfig, + ShadowView startingView, + ShadowView finalView) const { + ComponentDescriptor const &componentDescriptor = + getComponentDescriptorForShadowView(startingView); + auto mutatedShadowView = ShadowView(startingView); + + // Animate opacity or scale/transform + mutatedShadowView.props = componentDescriptor.interpolateProps( + progress, startingView.props, finalView.props); + + // 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; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h new file mode 100644 index 00000000000000..444e310c454675 --- /dev/null +++ b/ReactCommon/fabric/animations/LayoutAnimationKeyFrameManager.h @@ -0,0 +1,186 @@ +/* + * 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 +#include +#include +#include + +namespace facebook { +namespace react { + +// This corresponds exactly with JS. +enum class AnimationType { + 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; + AnimationProperty animationProperty; + double duration; // these are perhaps better represented as uint64_t, but they + // come from JS as doubles + double delay; + double springDamping; + double initialVelocity; +}; + +// 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; +}; + +struct LayoutAnimation { + SurfaceId surfaceId; + uint64_t startTime; + bool completed = false; + LayoutAnimationConfig layoutAnimationConfig; + std::shared_ptr successCallback; + std::shared_ptr errorCallback; + std::vector keyFrames; +}; + +class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, + public MountingOverrideDelegate { + public: + LayoutAnimationKeyFrameManager(LayoutAnimationStatusDelegate *delegate) + : layoutAnimationStatusDelegate_(delegate) { + // This is the ONLY place where we set or access + // layoutAnimationStatusDelegate_ without a mutex. + } + ~LayoutAnimationKeyFrameManager() {} + + void uiManagerDidConfigureNextLayoutAnimation( + RawValue const &config, + std::shared_ptr successCallback, + std::shared_ptr errorCallback) const override; + void setComponentDescriptorRegistry(SharedComponentDescriptorRegistry const & + componentDescriptorRegistry) override; + + // TODO: add SurfaceId to this API as well + bool shouldAnimateFrame() const override; + + 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. + better::optional pullTransaction( + SurfaceId surfaceId, + MountingTransaction::Number number, + MountingTelemetry const &telemetry, + ShadowViewMutationList mutations) const override; + + // 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: + mutable std::mutex layoutAnimationStatusDelegateMutex_; + mutable LayoutAnimationStatusDelegate *layoutAnimationStatusDelegate_{}; + + void adjustDelayedMutationIndicesForMutation( + SurfaceId surfaceId, + ShadowViewMutation const &mutation) const; + + protected: + bool mutatedViewIsVirtual(ShadowViewMutation const &mutation) const; + + ComponentDescriptor const &getComponentDescriptorForShadowView( + ShadowView const &shadowView) const; + std::pair calculateAnimationProgress( + uint64_t now, + LayoutAnimation const &animation, + AnimationConfig const &mutationConfig) const; + + ShadowView createInterpolatedShadowView( + double progress, + AnimationConfig const &animationConfig, + ShadowView startingView, + ShadowView finalView) const; + + virtual void animationMutationsForFrame( + SurfaceId surfaceId, + 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. + */ + mutable std::vector inflightAnimations_{}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/attributedstring/BUCK b/ReactCommon/fabric/attributedstring/BUCK index cb6d8c234116b8..f65fe7e2611b01 100644 --- a/ReactCommon/fabric/attributedstring/BUCK +++ b/ReactCommon/fabric/attributedstring/BUCK @@ -37,11 +37,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/attributedstring/conversions.h b/ReactCommon/fabric/attributedstring/conversions.h index 774913dcd326e9..3046e8f79830d3 100644 --- a/ReactCommon/fabric/attributedstring/conversions.h +++ b/ReactCommon/fabric/attributedstring/conversions.h @@ -611,9 +611,9 @@ inline folly::dynamic toDynamic(const AttributedString &attributedString) { if (fragment.isAttachment()) { dynamicFragment["isAttachment"] = true; dynamicFragment["width"] = - (int)fragment.parentShadowView.layoutMetrics.frame.size.width; + fragment.parentShadowView.layoutMetrics.frame.size.width; dynamicFragment["height"] = - (int)fragment.parentShadowView.layoutMetrics.frame.size.height; + fragment.parentShadowView.layoutMetrics.frame.size.height; } dynamicFragment["textAttributes"] = toDynamic(fragment.textAttributes); fragments.push_back(dynamicFragment); diff --git a/ReactCommon/fabric/componentregistry/BUCK b/ReactCommon/fabric/componentregistry/BUCK index 1eec505f7e2cdf..d993cb0e59d7c9 100644 --- a/ReactCommon/fabric/componentregistry/BUCK +++ b/ReactCommon/fabric/componentregistry/BUCK @@ -36,11 +36,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/components/activityindicator/BUCK b/ReactCommon/fabric/components/activityindicator/BUCK index b0eaa75eae36a7..47d57870130a0c 100644 --- a/ReactCommon/fabric/components/activityindicator/BUCK +++ b/ReactCommon/fabric/components/activityindicator/BUCK @@ -35,10 +35,9 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/components/art/ARTBaseShadowNode.h b/ReactCommon/fabric/components/art/ARTBaseShadowNode.h new file mode 100644 index 00000000000000..e674551dc23d4e --- /dev/null +++ b/ReactCommon/fabric/components/art/ARTBaseShadowNode.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 + +namespace facebook { +namespace react { + +class ARTBaseShadowNode { + public: + int test; + virtual ARTElement::Shared getARTElement() const = 0; + + static void buildElements( + ShadowNode const &parentNode, + ARTElement::ListOfShared &outNodes) { + for (auto const &child : parentNode.getChildren()) { + auto baseShadowNode = + std::dynamic_pointer_cast(child); + if (baseShadowNode) { + outNodes.push_back(baseShadowNode->getARTElement()); + } + } + } +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/BUCK b/ReactCommon/fabric/components/art/BUCK new file mode 100644 index 00000000000000..50ba2970986320 --- /dev/null +++ b/ReactCommon/fabric/components/art/BUCK @@ -0,0 +1,94 @@ +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", + "fb_xplat_cxx_test", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "art", + srcs = glob( + ["**/*.cpp"], + exclude = glob(["tests/**/*.cpp"]), + ), + headers = glob( + ["**/*.h"], + exclude = glob(["tests/**/*.h"]), + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ("group", "*.h"), + ("shape", "*.h"), + ("state", "*.h"), + ("surfaceview", "*.h"), + ("text", "*.h"), + ], + prefix = "react/components/art", + ), + 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(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.playground"], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + visibility = ["PUBLIC"], + deps = [ + "//third-party/glog:glog", + "//xplat/fbsystrace:fbsystrace", + "//xplat/folly:container_evicting_cache_map", + "//xplat/folly:headers_only", + "//xplat/folly:memory", + "//xplat/folly:molly", + YOGA_CXX_TARGET, + react_native_xplat_target("utils:utils"), + react_native_xplat_target("fabric/core:core"), + react_native_xplat_target("fabric/debug:debug"), + react_native_xplat_target("fabric/graphics:graphics"), + react_native_xplat_target("fabric/uimanager:uimanager"), + ], +) + +fb_xplat_cxx_test( + name = "tests", + srcs = glob(["tests/**/*.cpp"]), + headers = glob(["tests/**/*.h"]), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++14", + "-Wall", + ], + contacts = ["oncall+react_native@xmail.facebook.com"], + platforms = ( + # ANDROID, + # APPLE, + CXX + ), + deps = [ + ":art", + "//xplat/folly:molly", + "//xplat/third-party/gmock:gtest", + ], +) diff --git a/ReactCommon/fabric/components/art/conversions.h b/ReactCommon/fabric/components/art/conversions.h new file mode 100644 index 00000000000000..b234c6d0dbdfe8 --- /dev/null +++ b/ReactCommon/fabric/components/art/conversions.h @@ -0,0 +1,137 @@ +/* + * 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 { +namespace react { + +#ifdef ANDROID + +inline folly::dynamic toDynamic(ARTGroup const &group); +inline folly::dynamic toDynamic(ARTShape const &shape); +inline folly::dynamic toDynamic(ARTText const &text); +inline folly::dynamic toDynamic(ARTElement const &element); + +inline folly::dynamic toDynamic(std::vector const &elements) { + folly::dynamic result = folly::dynamic::array(); + for (auto const &element : elements) { + result.push_back(element); + } + return result; +} + +inline void addOptionalKey( + folly::dynamic &map, + std::string const &key, + std::vector const &values) { + if (values.size() > 0) { + map[key] = toDynamic(values); + } +} + +inline folly::dynamic toDynamic(ARTElement::ListOfShared const &elements) { + folly::dynamic children = folly::dynamic::array(); + for (auto const &element : elements) { + children.push_back(element->getDynamic()); + } + return children; +} + +inline folly::dynamic toDynamic(ARTGroup const &group) { + folly::dynamic result = folly::dynamic::object(); + result["opacity"] = group.opacity; + result["type"] = 1; + if (group.elements.size() > 0) { + result["elements"] = toDynamic(group.elements); + } + addOptionalKey(result, "clipping", group.clipping); + result["transform"] = toDynamic(group.transform); + return result; +} + +inline folly::dynamic toDynamic(ARTShape const &shape) { + folly::dynamic result = folly::dynamic::object(); + result["type"] = 2; + result["opacity"] = shape.opacity; + result["transform"] = toDynamic(shape.transform); + addOptionalKey(result, "d", shape.d); + addOptionalKey(result, "stroke", shape.stroke); + addOptionalKey(result, "strokeDash", shape.strokeDash); + addOptionalKey(result, "fill", shape.fill); + result["strokeWidth"] = shape.strokeWidth; + result["strokeCap"] = shape.strokeCap; + result["strokeJoin"] = shape.strokeJoin; + return result; +} + +inline folly::dynamic toDynamic(ARTTextAlignment const &aligment) { + switch (aligment) { + case ARTTextAlignment::Right: + return 1; + break; + case ARTTextAlignment::Center: + return 2; + break; + case ARTTextAlignment::Default: + default: + return 0; + break; + } +} + +inline folly::dynamic toDynamic(ARTTextFrame const &frame) { + folly::dynamic result = folly::dynamic::object(); + folly::dynamic font = folly::dynamic::object(); + font["fontSize"] = frame.font.fontSize; + font["fontStyle"] = frame.font.fontStyle; + font["fontFamily"] = frame.font.fontFamily; + font["fontWeight"] = frame.font.fontWeight; + result["font"] = font; + auto lines = frame.lines; + if (lines.size() > 0) { + folly::dynamic serializedLines = folly::dynamic::array(); + for (int i = 0; i < lines.size(); i++) { + serializedLines.push_back(lines[i]); + } + result["lines"] = serializedLines; + } + return result; +} + +inline folly::dynamic toDynamic(ARTText const &text) { + folly::dynamic result = folly::dynamic::object(); + result["type"] = 3; + result["opacity"] = text.opacity; + result["transform"] = toDynamic(text.transform); + addOptionalKey(result, "d", text.d); + addOptionalKey(result, "stroke", text.stroke); + addOptionalKey(result, "strokeDash", text.strokeDash); + addOptionalKey(result, "fill", text.fill); + result["strokeWidth"] = text.strokeWidth; + result["strokeCap"] = text.strokeCap; + result["strokeJoin"] = text.strokeJoin; + result["alignment"] = toDynamic(text.alignment); + result["frame"] = toDynamic(text.frame); + return result; +} + +inline folly::dynamic toDynamic(ARTSurfaceViewState const &surfaceViewState) { + folly::dynamic result = folly::dynamic::object(); + result["elements"] = toDynamic(surfaceViewState.elements); + return result; +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h b/ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h new file mode 100644 index 00000000000000..d2c3bd9424ce03 --- /dev/null +++ b/ReactCommon/fabric/components/art/group/ARTGroupComponentDescriptor.h @@ -0,0 +1,20 @@ +/* + * 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 { + +using ARTGroupComponentDescriptor = + ConcreteComponentDescriptor; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/group/ARTGroupProps.cpp b/ReactCommon/fabric/components/art/group/ARTGroupProps.cpp new file mode 100644 index 00000000000000..04e9c60237656d --- /dev/null +++ b/ReactCommon/fabric/components/art/group/ARTGroupProps.cpp @@ -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. + */ + +#include +#include + +namespace facebook { +namespace react { + +ARTGroupProps::ARTGroupProps( + const ARTGroupProps &sourceProps, + const RawProps &rawProps) + : Props(sourceProps, rawProps), + opacity(convertRawProp(rawProps, "opacity", sourceProps.opacity, {1.0})), + transform( + convertRawProp(rawProps, "transform", sourceProps.transform, {})), + clipping( + convertRawProp(rawProps, "clipping", sourceProps.clipping, {})){}; + +#pragma mark - DebugStringConvertible + +#if RN_DEBUG_STRING_CONVERTIBLE +SharedDebugStringConvertibleList RawTextProps::getDebugProps() const { + return {debugStringConvertibleItem("opacity", opacity)}; +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/group/ARTGroupProps.h b/ReactCommon/fabric/components/art/group/ARTGroupProps.h new file mode 100644 index 00000000000000..2827288613fe38 --- /dev/null +++ b/ReactCommon/fabric/components/art/group/ARTGroupProps.h @@ -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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class ARTGroupProps; + +class ARTGroupProps : public Props { + public: + ARTGroupProps() = default; + ARTGroupProps(const ARTGroupProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + Float opacity{1.0}; + std::vector transform{}; + std::vector clipping{}; + +#pragma mark - DebugStringConvertible +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.cpp b/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.cpp new file mode 100644 index 00000000000000..7d5301a53c43b2 --- /dev/null +++ b/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.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 +#include +#include +#include + +namespace facebook { +namespace react { + +extern const char ARTGroupComponentName[] = "ARTGroup"; + +ARTElement::Shared ARTGroupShadowNode::getARTElement() const { + auto elements = ARTElement::ListOfShared{}; + for (auto const &child : getChildren()) { + auto node = std::dynamic_pointer_cast(child); + if (node) { + elements.push_back(node->getARTElement()); + } + } + + auto props = getConcreteProps(); + return std::make_shared( + props.opacity, props.transform, elements, props.clipping); +} +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.h b/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.h new file mode 100644 index 00000000000000..dd85dffe66eaae --- /dev/null +++ b/ReactCommon/fabric/components/art/group/ARTGroupShadowNode.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 + +namespace facebook { +namespace react { + +extern const char ARTGroupComponentName[]; + +/* + * `ShadowNode` for component. + */ +class ARTGroupShadowNode : public ConcreteShadowNode< + ARTGroupComponentName, + ShadowNode, + ARTGroupProps>, + public ARTBaseShadowNode { + public: + using ConcreteShadowNode::ConcreteShadowNode; + + virtual ARTElement::Shared getARTElement() const override; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h b/ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h new file mode 100644 index 00000000000000..5d65696f7dbaf2 --- /dev/null +++ b/ReactCommon/fabric/components/art/shape/ARTShapeComponentDescriptor.h @@ -0,0 +1,20 @@ +/* + * 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 { + +using ARTShapeComponentDescriptor = + ConcreteComponentDescriptor; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp b/ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp new file mode 100644 index 00000000000000..9b1c79c00aedce --- /dev/null +++ b/ReactCommon/fabric/components/art/shape/ARTShapeProps.cpp @@ -0,0 +1,49 @@ +/* + * 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 { + +ARTShapeProps::ARTShapeProps( + const ARTShapeProps &sourceProps, + const RawProps &rawProps) + : Props(sourceProps, rawProps), + + opacity(convertRawProp(rawProps, "opacity", sourceProps.opacity, {1.0})), + transform( + convertRawProp(rawProps, "transform", sourceProps.transform, {})), + d(convertRawProp(rawProps, "d", sourceProps.d, {})), + stroke(convertRawProp(rawProps, "stroke", sourceProps.stroke, {})), + strokeDash( + convertRawProp(rawProps, "strokeDash", sourceProps.strokeDash, {})), + fill(convertRawProp(rawProps, "fill", sourceProps.fill, {})), + strokeWidth(convertRawProp( + rawProps, + "strokeWidth", + sourceProps.strokeWidth, + {1.0})), + strokeCap( + convertRawProp(rawProps, "strokeCap", sourceProps.strokeCap, {1})), + strokeJoin( + convertRawProp(rawProps, "strokeJoin", sourceProps.strokeJoin, {1})){ + + }; + +#pragma mark - DebugStringConvertible + +#if RN_DEBUG_STRING_CONVERTIBLE +SharedDebugStringConvertibleList RawTextProps::getDebugProps() const { + return {debugStringConvertibleItem("opacity", opacity)}; +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeProps.h b/ReactCommon/fabric/components/art/shape/ARTShapeProps.h new file mode 100644 index 00000000000000..7323f7ccd07b25 --- /dev/null +++ b/ReactCommon/fabric/components/art/shape/ARTShapeProps.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 +#include + +namespace facebook { +namespace react { + +class ARTShapeProps; + +class ARTShapeProps : public Props { + public: + ARTShapeProps() = default; + ARTShapeProps(const ARTShapeProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + Float opacity{1.0}; + std::vector transform{}; + std::vector d{}; + std::vector stroke{}; + std::vector strokeDash{}; + std::vector fill{}; + Float strokeWidth{1.0}; + int strokeCap{1}; + int strokeJoin{1}; + +#pragma mark - DebugStringConvertible + +#if RN_DEBUG_STRING_CONVERTIBLE + SharedDebugStringConvertibleList getDebugProps() const override; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp b/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp new file mode 100644 index 00000000000000..cb11199fd06576 --- /dev/null +++ b/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.cpp @@ -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. + */ + +#include "ARTShapeShadowNode.h" +#include +#include +namespace facebook { +namespace react { + +extern const char ARTShapeComponentName[] = "ARTShape"; + +ARTElement::Shared ARTShapeShadowNode::getARTElement() const { + auto props = getConcreteProps(); + return std::make_shared( + props.opacity, + props.transform, + props.d, + props.stroke, + props.strokeDash, + props.fill, + props.strokeWidth, + props.strokeCap, + props.strokeJoin); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h b/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h new file mode 100644 index 00000000000000..0373aef45cf34c --- /dev/null +++ b/ReactCommon/fabric/components/art/shape/ARTShapeShadowNode.h @@ -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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +extern const char ARTShapeComponentName[]; + +/* + * `ShadowNode` for component. + */ +class ARTShapeShadowNode : public ConcreteShadowNode< + ARTShapeComponentName, + ShadowNode, + ARTShapeProps>, + public ARTBaseShadowNode { + public: + using ConcreteShadowNode::ConcreteShadowNode; + + virtual ARTElement::Shared getARTElement() const override; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerMode.java b/ReactCommon/fabric/components/art/state/ARTElement.cpp similarity index 59% rename from ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerMode.java rename to ReactCommon/fabric/components/art/state/ARTElement.cpp index d6c28ebd348db8..5b1ed74cb61c3a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/timepicker/TimePickerMode.java +++ b/ReactCommon/fabric/components/art/state/ARTElement.cpp @@ -5,10 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -package com.facebook.react.modules.timepicker; +#include -public enum TimePickerMode { - CLOCK, - SPINNER, - DEFAULT -} +namespace facebook { +namespace react {} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTElement.h b/ReactCommon/fabric/components/art/state/ARTElement.h new file mode 100644 index 00000000000000..1b3b82b1685a9e --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTElement.h @@ -0,0 +1,81 @@ +/* + * 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 +#include +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * Simple, cross-platfrom, React-specific implementation of base ART Element + */ +class ARTElement { + public: + using Shared = std::shared_ptr; + using ListOfShared = better::small_vector; + + ARTElement() = default; + ARTElement( + ARTElementType elementType, + Float opacity, + std::vector transform) + : elementType(elementType), opacity(opacity), transform(transform){}; + virtual ~ARTElement(){}; + + ARTElementType elementType; + Float opacity; + std::vector transform; + + virtual bool operator==(const ARTElement &rhs) const = 0; + virtual bool operator!=(const ARTElement &rhs) const = 0; + friend bool operator==(ListOfShared e1, ListOfShared e2) { + bool equals = e1.size() == e2.size(); + for (int i = 0; i < equals && e1.size(); i++) { + // Pointer equality - this will work if both are pointing at the same + // object, or both are nullptr + if (e1[i] == e2[i]) { + continue; + } + + // Get pointers from both + // If one is null, we know they can't both be null because of the above + // check + auto ptr1 = e1[i].get(); + auto ptr2 = e2[i].get(); + if (ptr1 == nullptr || ptr2 == nullptr) { + equals = false; + break; + } + + // Dereference and compare objects + if (*ptr1 != *ptr2) { + equals = false; + break; + } + } + + return equals; + }; + +#ifdef ANDROID + virtual folly::dynamic getDynamic() const = 0; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTGroup.cpp b/ReactCommon/fabric/components/art/state/ARTGroup.cpp new file mode 100644 index 00000000000000..f3a8c6ee59b5c6 --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTGroup.cpp @@ -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. + */ + +#include +#include +#include + +namespace facebook { +namespace react { + +bool ARTGroup::operator==(const ARTElement &rhs) const { + if (rhs.elementType != ARTElementType::Group) { + return false; + } + auto group = (const ARTGroup &)(rhs); + return std::tie(elementType, opacity, transform, clipping) == + std::tie( + group.elementType, + group.opacity, + group.transform, + group.clipping) && + elements == group.elements; +} + +bool ARTGroup::operator!=(const ARTElement &rhs) const { + return !(*this == rhs); +} + +#ifdef ANDROID +folly::dynamic ARTGroup::getDynamic() const { + return toDynamic(*this); +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTGroup.h b/ReactCommon/fabric/components/art/state/ARTGroup.h new file mode 100644 index 00000000000000..fa86e7923655c0 --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTGroup.h @@ -0,0 +1,49 @@ +/* + * 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 { + +/* + * Simple, cross-platfrom, React-specific implementation of ART Group element + */ +class ARTGroup : public ARTElement { + public: + using Shared = std::shared_ptr; + ARTGroup( + Float opacity, + std::vector transform, + ARTGroup::ListOfShared elements, + std::vector clipping) + : ARTElement(ARTElementType::Group, opacity, transform), + elements(elements), + clipping(clipping){}; + ARTGroup() = default; + virtual ~ARTGroup(){}; + + ARTElement::ListOfShared elements{}; + + std::vector clipping{}; + + bool operator==(const ARTElement &rhs) const override; + bool operator!=(const ARTElement &rhs) const override; + +#ifdef ANDROID + folly::dynamic getDynamic() const override; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTShape.cpp b/ReactCommon/fabric/components/art/state/ARTShape.cpp new file mode 100644 index 00000000000000..31fac9ffeb3e79 --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTShape.cpp @@ -0,0 +1,55 @@ +/* + * 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 { + +bool ARTShape::operator==(const ARTElement &rhs) const { + if (rhs.elementType != ARTElementType::Shape) { + return false; + } + auto shape = (const ARTShape &)(rhs); + return std::tie( + elementType, + opacity, + transform, + d, + stroke, + strokeDash, + fill, + strokeWidth, + strokeCap, + strokeJoin) == + std::tie( + shape.elementType, + shape.opacity, + shape.transform, + shape.d, + shape.stroke, + shape.strokeDash, + shape.fill, + shape.strokeWidth, + shape.strokeCap, + shape.strokeJoin); +} + +bool ARTShape::operator!=(const ARTElement &rhs) const { + return !(*this == rhs); +} + +#ifdef ANDROID +folly::dynamic ARTShape::getDynamic() const { + return toDynamic(*this); +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTShape.h b/ReactCommon/fabric/components/art/state/ARTShape.h new file mode 100644 index 00000000000000..e50cf966a061ed --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTShape.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 + +namespace facebook { +namespace react { + +/* + * Simple, cross-platfrom, React-specific implementation of ART Shape Element + */ +class ARTShape : public ARTElement { + public: + using Shared = std::shared_ptr; + ARTShape( + Float opacity, + std::vector transform, + std::vector d, + std::vector stroke, + std::vector strokeDash, + std::vector fill, + Float strokeWidth, + int strokeCap, + int strokeJoin) + : ARTElement(ARTElementType::Shape, opacity, transform), + d(d), + stroke(stroke), + strokeDash(strokeDash), + fill(fill), + strokeWidth(strokeWidth), + strokeCap(strokeCap), + strokeJoin(strokeJoin){}; + ARTShape() = default; + virtual ~ARTShape(){}; + + std::vector d{}; + std::vector stroke{}; + std::vector strokeDash{}; + std::vector fill{}; + Float strokeWidth{1.0}; + int strokeCap{1}; + int strokeJoin{1}; + + bool operator==(const ARTElement &rhs) const override; + bool operator!=(const ARTElement &rhs) const override; + +#ifdef ANDROID + folly::dynamic getDynamic() const override; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTText.cpp b/ReactCommon/fabric/components/art/state/ARTText.cpp new file mode 100644 index 00000000000000..1a061554134e77 --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTText.cpp @@ -0,0 +1,59 @@ +/* + * 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 { + +bool ARTText::operator==(const ARTElement &rhs) const { + if (rhs.elementType != ARTElementType::Text) { + return false; + } + auto text = (const ARTText &)(rhs); + return std::tie( + elementType, + opacity, + transform, + d, + stroke, + strokeDash, + fill, + strokeWidth, + strokeCap, + strokeJoin, + alignment, + frame) == + std::tie( + text.elementType, + text.opacity, + text.transform, + text.d, + text.stroke, + text.strokeDash, + text.fill, + text.strokeWidth, + text.strokeCap, + text.strokeJoin, + text.alignment, + text.frame); +} + +bool ARTText::operator!=(const ARTElement &rhs) const { + return !(*this == rhs); +} + +#ifdef ANDROID +folly::dynamic ARTText::getDynamic() const { + return toDynamic(*this); +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/ARTText.h b/ReactCommon/fabric/components/art/state/ARTText.h new file mode 100644 index 00000000000000..b01da694a377b4 --- /dev/null +++ b/ReactCommon/fabric/components/art/state/ARTText.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 +#include +#include + +#include + +namespace facebook { +namespace react { + +/* + * Simple, cross-platfrom, React-specific implementation of ART Text Element + */ +class ARTText : public ARTShape { + public: + using ARTShared = std::shared_ptr; + ARTText( + Float opacity, + std::vector transform, + std::vector d, + std::vector stroke, + std::vector strokeDash, + std::vector fill, + Float strokeWidth, + int strokeCap, + int strokeJoin, + ARTTextAlignment alignment, + ARTTextFrame frame) + : ARTShape( + opacity, + transform, + d, + stroke, + strokeDash, + fill, + strokeWidth, + strokeCap, + strokeJoin), + alignment(alignment), + frame(frame) { + elementType = ARTElementType::Text; + }; + ARTText() = default; + virtual ~ARTText(){}; + + bool operator==(const ARTElement &rhs) const override; + bool operator!=(const ARTElement &rhs) const override; + + ARTTextAlignment alignment{ARTTextAlignment::Default}; + ARTTextFrame frame{}; + +#ifdef ANDROID + folly::dynamic getDynamic() const override; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/state/primitives.h b/ReactCommon/fabric/components/art/state/primitives.h new file mode 100644 index 00000000000000..677fcf9ce1120e --- /dev/null +++ b/ReactCommon/fabric/components/art/state/primitives.h @@ -0,0 +1,56 @@ +/* + * 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 { + +enum class ARTElementType { Shape, Text, Group }; + +enum class ARTTextAlignment { Default, Right, Center }; + +struct ARTTextFrameFont { + Float fontSize; + std::string fontStyle; + std::string fontFamily; + std::string fontWeight; + + bool operator==(const ARTTextFrameFont &rhs) const { + return std::tie( + this->fontSize, + this->fontStyle, + this->fontFamily, + this->fontWeight) == + std::tie(rhs.fontSize, rhs.fontStyle, rhs.fontFamily, rhs.fontWeight); + } + + bool operator!=(const ARTTextFrameFont &rhs) const { + return !(*this == rhs); + } +}; + +struct ARTTextFrame { + std::vector lines; + ARTTextFrameFont font; + + bool operator==(const ARTTextFrame &rhs) const { + return std::tie(this->lines, this->font) == std::tie(rhs.lines, rhs.font); + } + + bool operator!=(const ARTTextFrame &rhs) const { + return !(*this == rhs); + } +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewComponentDescriptor.h b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewComponentDescriptor.h new file mode 100644 index 00000000000000..c48ee29d468fd2 --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewComponentDescriptor.h @@ -0,0 +1,20 @@ +/* + * 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 { + +using ARTSurfaceViewComponentDescriptor = + ConcreteComponentDescriptor; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.cpp b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.cpp new file mode 100644 index 00000000000000..6969d93f1d29cd --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.cpp @@ -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. + */ + +#include +#include + +namespace facebook { +namespace react { + +ARTSurfaceViewProps::ARTSurfaceViewProps( + ARTSurfaceViewProps const &sourceProps, + RawProps const &rawProps) + : ViewProps(sourceProps, rawProps), + + backgroundColor(convertRawProp( + rawProps, + "backgroundColor", + sourceProps.backgroundColor, + {})) {} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.h b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.h new file mode 100644 index 00000000000000..c243686d506bb8 --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewProps.h @@ -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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class ARTSurfaceViewProps final : public ViewProps { + public: + ARTSurfaceViewProps() = default; + ARTSurfaceViewProps( + ARTSurfaceViewProps const &sourceProps, + RawProps const &rawProps); + +#pragma mark - Props + + SharedColor backgroundColor{}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.cpp b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.cpp new file mode 100644 index 00000000000000..5be367ba3b68b0 --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.cpp @@ -0,0 +1,47 @@ +/* + * 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 + +namespace facebook { +namespace react { + +using Content = ARTSurfaceViewShadowNode::Content; + +extern const char ARTSurfaceViewComponentName[] = "ARTSurfaceView"; + +void ARTSurfaceViewShadowNode::layout(LayoutContext layoutContext) { + ensureUnsealed(); + auto content = getContent(); + updateStateIfNeeded(content); +} + +Content const &ARTSurfaceViewShadowNode::getContent() const { + if (content_.has_value()) { + return content_.value(); + } + ensureUnsealed(); + auto elements = ARTElement::ListOfShared{}; + ARTBaseShadowNode::buildElements(*this, elements); + content_ = Content{elements}; + return content_.value(); +} + +void ARTSurfaceViewShadowNode::updateStateIfNeeded(Content const &content) { + ensureUnsealed(); + auto &state = getStateData(); + if (content.elements == state.elements) { + return; + } + setStateData(ARTSurfaceViewState{content.elements}); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.h b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.h new file mode 100644 index 00000000000000..0bd2cdaa43fb1e --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewShadowNode.h @@ -0,0 +1,62 @@ +/* + * 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 +#include + +namespace facebook { +namespace react { + +extern const char ARTSurfaceViewComponentName[]; + +using ARTSurfaceViewEventEmitter = ViewEventEmitter; + +/* + * `ShadowNode` for component, represents -like + * component containing that will be used to display ARTElements. + */ +class ARTSurfaceViewShadowNode : public ConcreteViewShadowNode< + ARTSurfaceViewComponentName, + ARTSurfaceViewProps, + ARTSurfaceViewEventEmitter, + ARTSurfaceViewState> { + public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; + + static ShadowNodeTraits BaseTraits() { + auto traits = ConcreteViewShadowNode::BaseTraits(); + traits.set(ShadowNodeTraits::Trait::LeafYogaNode); + return traits; + } + + class Content final { + public: + ARTElement::ListOfShared elements{}; + }; + + void layout(LayoutContext layoutContext) override; + + private: + Content const &getContent() const; + + void updateStateIfNeeded(Content const &content); + + /* + * Cached content of the subtree started from the node. + */ + mutable better::optional content_{}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.cpp b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.cpp new file mode 100644 index 00000000000000..44e47d45c04646 --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.cpp @@ -0,0 +1,22 @@ +/* + * 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 { + +#ifdef ANDROID +folly::dynamic ARTSurfaceViewState::getDynamic() const { + return toDynamic(*this); +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.h b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.h new file mode 100644 index 00000000000000..86eb3d0f5e8c58 --- /dev/null +++ b/ReactCommon/fabric/components/art/surfaceview/ARTSurfaceViewState.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 + +#ifdef ANDROID +#include +#endif + +namespace facebook { +namespace react { + +/* + * State for component. + * Represents what to render and how to render. + */ +class ARTSurfaceViewState final { + public: + ARTElement::ListOfShared elements{}; + +#ifdef ANDROID + ARTSurfaceViewState(ARTElement::ListOfShared const &elements) + : elements(elements) {} + ARTSurfaceViewState() = default; + ARTSurfaceViewState( + ARTSurfaceViewState const &previousState, + folly::dynamic const &data) { + assert(false && "Not supported"); + }; + folly::dynamic getDynamic() const; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/text/ARTTextComponentDescriptor.h b/ReactCommon/fabric/components/art/text/ARTTextComponentDescriptor.h new file mode 100644 index 00000000000000..ae839d71e40925 --- /dev/null +++ b/ReactCommon/fabric/components/art/text/ARTTextComponentDescriptor.h @@ -0,0 +1,20 @@ +/* + * 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 { + +using ARTTextComponentDescriptor = + ConcreteComponentDescriptor; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/text/ARTTextProps.cpp b/ReactCommon/fabric/components/art/text/ARTTextProps.cpp new file mode 100644 index 00000000000000..b7948e9a8feaec --- /dev/null +++ b/ReactCommon/fabric/components/art/text/ARTTextProps.cpp @@ -0,0 +1,49 @@ +/* + * 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 + +namespace facebook { +namespace react { + +ARTTextProps::ARTTextProps( + const ARTTextProps &sourceProps, + const RawProps &rawProps) + : Props(sourceProps, rawProps), + + opacity(convertRawProp(rawProps, "opacity", sourceProps.opacity, {1.0})), + transform( + convertRawProp(rawProps, "transform", sourceProps.transform, {})), + d(convertRawProp(rawProps, "d", sourceProps.d, {})), + stroke(convertRawProp(rawProps, "stroke", sourceProps.stroke, {})), + strokeDash( + convertRawProp(rawProps, "strokeDash", sourceProps.strokeDash, {})), + fill(convertRawProp(rawProps, "fill", sourceProps.fill, {})), + strokeWidth(convertRawProp( + rawProps, + "strokeWidth", + sourceProps.strokeWidth, + {1.0})), + strokeCap( + convertRawProp(rawProps, "strokeCap", sourceProps.strokeCap, {1})), + strokeJoin( + convertRawProp(rawProps, "strokeJoin", sourceProps.strokeJoin, {1})), + alignment( + convertRawProp(rawProps, "alignment", sourceProps.alignment, {})), + frame(convertRawProp(rawProps, "frame", sourceProps.frame, {})){}; + +#pragma mark - DebugStringConvertible + +#if RN_DEBUG_STRING_CONVERTIBLE +SharedDebugStringConvertibleList RawTextProps::getDebugProps() const { + return {debugStringConvertibleItem("opacity", opacity)}; +} +#endif + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/text/ARTTextProps.h b/ReactCommon/fabric/components/art/text/ARTTextProps.h new file mode 100644 index 00000000000000..087e56669cf1ef --- /dev/null +++ b/ReactCommon/fabric/components/art/text/ARTTextProps.h @@ -0,0 +1,110 @@ +/* + * 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 { + +static inline void fromRawValue( + const RawValue &value, + ARTTextFrameFont &result) { + auto map = (better::map)value; + auto fontSize = map.find("fontSize"); + if (fontSize != map.end()) { + fromRawValue(fontSize->second, result.fontSize); + } + auto fontStyle = map.find("fontStyle"); + if (fontStyle != map.end()) { + fromRawValue(fontStyle->second, result.fontStyle); + } + auto fontFamily = map.find("fontFamily"); + if (fontFamily != map.end()) { + fromRawValue(fontFamily->second, result.fontFamily); + } + auto fontWeight = map.find("fontWeight"); + if (fontWeight != map.end()) { + fromRawValue(fontWeight->second, result.fontWeight); + } +} + +static inline void fromRawValue( + const RawValue &value, + ARTTextAlignment &result) { + auto alignment = (int)value; + switch (alignment) { + case 1: + result = ARTTextAlignment::Right; + break; + case 2: + result = ARTTextAlignment::Center; + break; + default: + result = ARTTextAlignment::Default; + break; + } +} + +static inline std::string toString(const ARTTextFrameFont &value) { + return "[Object ARTTextFrameFont]"; +} + +static inline void fromRawValue(const RawValue &value, ARTTextFrame &result) { + auto map = (better::map)value; + + auto lines = map.find("lines"); + if (lines != map.end()) { + fromRawValue(lines->second, result.lines); + } + auto font = map.find("font"); + if (font != map.end()) { + fromRawValue(font->second, result.font); + } +} + +static inline std::string toString(const ARTTextFrame &value) { + return "[Object ARTTextFrame]"; +} + +class ARTTextProps; + +using SharedARTTextProps = std::shared_ptr; + +class ARTTextProps : public Props { + public: + ARTTextProps() = default; + ARTTextProps(const ARTTextProps &sourceProps, const RawProps &rawProps); + +#pragma mark - Props + + Float opacity{1.0}; + std::vector transform{}; + std::vector d{}; + std::vector stroke{}; + std::vector strokeDash{}; + std::vector fill{}; + Float strokeWidth{1.0}; + int strokeCap{1}; + int strokeJoin{1}; + ARTTextAlignment alignment{ARTTextAlignment::Default}; + ARTTextFrame frame{}; + +#pragma mark - DebugStringConvertible + +#if RN_DEBUG_STRING_CONVERTIBLE + SharedDebugStringConvertibleList getDebugProps() const override; +#endif +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/text/ARTTextShadowNode.cpp b/ReactCommon/fabric/components/art/text/ARTTextShadowNode.cpp new file mode 100644 index 00000000000000..aeb596e13348d3 --- /dev/null +++ b/ReactCommon/fabric/components/art/text/ARTTextShadowNode.cpp @@ -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. + */ + +#include "ARTTextShadowNode.h" +#include +#include + +namespace facebook { +namespace react { + +extern const char ARTTextComponentName[] = "ARTText"; + +ARTElement::Shared ARTTextShadowNode::getARTElement() const { + auto props = getConcreteProps(); + return std::make_shared( + props.opacity, + props.transform, + props.d, + props.stroke, + props.strokeDash, + props.fill, + props.strokeWidth, + props.strokeCap, + props.strokeJoin, + props.alignment, + props.frame); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/art/text/ARTTextShadowNode.h b/ReactCommon/fabric/components/art/text/ARTTextShadowNode.h new file mode 100644 index 00000000000000..93d3d4a6567ab1 --- /dev/null +++ b/ReactCommon/fabric/components/art/text/ARTTextShadowNode.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 +#include + +namespace facebook { +namespace react { + +extern const char ARTTextComponentName[]; + +/* + * `ShadowNode` for component. + */ +class ARTTextShadowNode + : public ConcreteShadowNode, + public ARTBaseShadowNode { + public: + using ConcreteShadowNode::ConcreteShadowNode; + + virtual ARTElement::Shared getARTElement() const override; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/image/BUCK b/ReactCommon/fabric/components/image/BUCK index 9f94284cf15630..43b8f54739565a 100644 --- a/ReactCommon/fabric/components/image/BUCK +++ b/ReactCommon/fabric/components/image/BUCK @@ -35,10 +35,9 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/components/image/ImageShadowNode.cpp b/ReactCommon/fabric/components/image/ImageShadowNode.cpp index 68d0667aa4e846..4a00f2c1bf23ca 100644 --- a/ReactCommon/fabric/components/image/ImageShadowNode.cpp +++ b/ReactCommon/fabric/components/image/ImageShadowNode.cpp @@ -45,19 +45,23 @@ void ImageShadowNode::updateStateIfNeeded() { ImageSource ImageShadowNode::getImageSource() const { auto sources = getConcreteProps().sources; - if (sources.size() == 0) { + if (sources.empty()) { return { /* .type = */ ImageSource::Type::Invalid, }; } - if (sources.size() == 1) { - return sources[0]; - } - auto layoutMetrics = getLayoutMetrics(); auto size = layoutMetrics.getContentFrame().size; auto scale = layoutMetrics.pointScaleFactor; + + if (sources.size() == 1) { + auto source = sources[0]; + source.size = size; + source.scale = scale; + return source; + } + auto targetImageArea = size.width * size.height * scale * scale; auto bestFit = std::numeric_limits::infinity(); @@ -77,6 +81,9 @@ ImageSource ImageShadowNode::getImageSource() const { } } + bestSource.size = size; + bestSource.scale = scale; + return bestSource; } diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/BUCK b/ReactCommon/fabric/components/legacyviewmanagerinterop/BUCK index 3c1e5c09748c30..3cd958bb17889e 100644 --- a/ReactCommon/fabric/components/legacyviewmanagerinterop/BUCK +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/BUCK @@ -34,9 +34,9 @@ rn_xplat_cxx_library( "-Wall", ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm index 7e2c3813676cf0..a5921741440945 100644 --- a/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropComponentDescriptor.mm @@ -24,6 +24,10 @@ if (componentName == "StickerInputView") { return "FBStickerInputViewManager"; } + + if (componentName == "FBRotatablePhotoPlayer") { + return "FBRotatablePhotoPlayerViewManager"; + } std::string fbPrefix("FB"); if (std::mismatch(fbPrefix.begin(), fbPrefix.end(), componentName.begin()).first == fbPrefix.end()) { // If `moduleName` has "FB" prefix. diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index 1874f8d5d1c45f..d8f1b4abf4ed7b 100644 --- a/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -20,7 +20,7 @@ @implementation RCTLegacyViewManagerInteropCoordinator { RCTComponentData *_componentData; - RCTBridge *_bridge; + __weak RCTBridge *_bridge; /* Each instnace 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. diff --git a/ReactCommon/fabric/components/modal/BUCK b/ReactCommon/fabric/components/modal/BUCK index a84b80df2a456e..5913667729910f 100644 --- a/ReactCommon/fabric/components/modal/BUCK +++ b/ReactCommon/fabric/components/modal/BUCK @@ -48,12 +48,10 @@ rn_xplat_cxx_library( fbandroid_headers = glob( ["platform/android/*.h"], ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( ["platform/android/*.cpp"], ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, ios_exported_headers = subdir_glob( @@ -63,6 +61,7 @@ rn_xplat_cxx_library( ], prefix = "react/components/modal", ), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/components/picker/BUCK b/ReactCommon/fabric/components/picker/BUCK index 8508262ce704d0..280aed2d07ccbc 100644 --- a/ReactCommon/fabric/components/picker/BUCK +++ b/ReactCommon/fabric/components/picker/BUCK @@ -44,11 +44,10 @@ rn_xplat_cxx_library( fbandroid_deps = [ react_native_target("jni/react/jni:jni"), ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/root/BUCK b/ReactCommon/fabric/components/root/BUCK index 5805cff81df76a..bef67507464974 100644 --- a/ReactCommon/fabric/components/root/BUCK +++ b/ReactCommon/fabric/components/root/BUCK @@ -35,10 +35,9 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/components/safeareaview/BUCK b/ReactCommon/fabric/components/safeareaview/BUCK index 4edd42c67573da..033f987770f11d 100644 --- a/ReactCommon/fabric/components/safeareaview/BUCK +++ b/ReactCommon/fabric/components/safeareaview/BUCK @@ -31,9 +31,9 @@ rn_xplat_cxx_library( "-Wall", ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.cpp b/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.cpp index a04bea2e5e6edd..bf750e84f504af 100644 --- a/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.cpp +++ b/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.cpp @@ -12,5 +12,13 @@ namespace react { extern const char SafeAreaViewComponentName[] = "SafeAreaView"; +SafeAreaViewShadowNode::SafeAreaViewShadowNode( + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment) + : ConcreteViewShadowNode(sourceShadowNode, fragment), + alreadyAppliedPadding( + static_cast(sourceShadowNode) + .alreadyAppliedPadding) {} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.h b/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.h index 27df74ffaba387..1f09c463b63606 100644 --- a/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.h +++ b/ReactCommon/fabric/components/safeareaview/SafeAreaViewShadowNode.h @@ -29,6 +29,10 @@ class SafeAreaViewShadowNode final : public ConcreteViewShadowNode< public: EdgeInsets alreadyAppliedPadding{}; + + SafeAreaViewShadowNode( + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment); }; } // namespace react diff --git a/ReactCommon/fabric/components/scrollview/BUCK b/ReactCommon/fabric/components/scrollview/BUCK index 5f59cfba1a4768..8ffd81f4003892 100644 --- a/ReactCommon/fabric/components/scrollview/BUCK +++ b/ReactCommon/fabric/components/scrollview/BUCK @@ -38,11 +38,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/components/scrollview/ScrollViewEventEmitter.cpp b/ReactCommon/fabric/components/scrollview/ScrollViewEventEmitter.cpp index b8c7e708616a62..a44150d2183fd6 100644 --- a/ReactCommon/fabric/components/scrollview/ScrollViewEventEmitter.cpp +++ b/ReactCommon/fabric/components/scrollview/ScrollViewEventEmitter.cpp @@ -60,7 +60,9 @@ static jsi::Value scrollViewMetricsPayload( void ScrollViewEventEmitter::onScroll( const ScrollViewMetrics &scrollViewMetrics) const { - dispatchScrollViewEvent("scroll", scrollViewMetrics); + dispatchUniqueEvent("scroll", [scrollViewMetrics](jsi::Runtime &runtime) { + return scrollViewMetricsPayload(runtime, scrollViewMetrics); + }); } void ScrollViewEventEmitter::onScrollBeginDrag( diff --git a/ReactCommon/fabric/components/scrollview/ScrollViewProps.cpp b/ReactCommon/fabric/components/scrollview/ScrollViewProps.cpp index fdc3c113e20b51..fdedc739e7c058 100644 --- a/ReactCommon/fabric/components/scrollview/ScrollViewProps.cpp +++ b/ReactCommon/fabric/components/scrollview/ScrollViewProps.cpp @@ -17,8 +17,8 @@ namespace facebook { namespace react { ScrollViewProps::ScrollViewProps( - const ScrollViewProps &sourceProps, - const RawProps &rawProps) + ScrollViewProps const &sourceProps, + RawProps const &rawProps) : ViewProps(sourceProps, rawProps), alwaysBounceHorizontal(convertRawProp( rawProps, @@ -126,6 +126,11 @@ ScrollViewProps::ScrollViewProps( "contentInset", sourceProps.contentInset, {})), + contentOffset(convertRawProp( + rawProps, + "contentOffset", + sourceProps.contentOffset, + {})), scrollIndicatorInsets(convertRawProp( rawProps, "scrollIndicatorInsets", @@ -232,6 +237,10 @@ SharedDebugStringConvertibleList ScrollViewProps::getDebugProps() const { "contentInset", contentInset, defaultScrollViewProps.contentInset), + debugStringConvertibleItem( + "contentOffset", + contentOffset, + defaultScrollViewProps.contentOffset), debugStringConvertibleItem( "scrollIndicatorInsets", scrollIndicatorInsets, diff --git a/ReactCommon/fabric/components/scrollview/ScrollViewProps.h b/ReactCommon/fabric/components/scrollview/ScrollViewProps.h index 14e5c5a88bd9bc..e520c1736347bc 100644 --- a/ReactCommon/fabric/components/scrollview/ScrollViewProps.h +++ b/ReactCommon/fabric/components/scrollview/ScrollViewProps.h @@ -17,35 +17,36 @@ namespace react { class ScrollViewProps final : public ViewProps { public: ScrollViewProps() = default; - ScrollViewProps(const ScrollViewProps &sourceProps, const RawProps &rawProps); + ScrollViewProps(ScrollViewProps const &sourceProps, RawProps const &rawProps); #pragma mark - Props - const bool alwaysBounceHorizontal{}; - const bool alwaysBounceVertical{}; - const bool bounces{true}; - const bool bouncesZoom{true}; - const bool canCancelContentTouches{true}; - const bool centerContent{}; - const bool automaticallyAdjustContentInsets{}; - const Float decelerationRate{0.998}; - const bool directionalLockEnabled{}; - const ScrollViewIndicatorStyle indicatorStyle{}; - const ScrollViewKeyboardDismissMode keyboardDismissMode{}; - const Float maximumZoomScale{1.0}; - const Float minimumZoomScale{1.0}; - const bool scrollEnabled{true}; - const bool pagingEnabled{}; - const bool pinchGestureEnabled{true}; - const bool scrollsToTop{true}; - const bool showsHorizontalScrollIndicator{true}; - const bool showsVerticalScrollIndicator{true}; - const Float scrollEventThrottle{}; - const Float zoomScale{1.0}; - const EdgeInsets contentInset{}; - const EdgeInsets scrollIndicatorInsets{}; - const Float snapToInterval{}; - const ScrollViewSnapToAlignment snapToAlignment{}; + bool const alwaysBounceHorizontal{}; + bool const alwaysBounceVertical{}; + bool const bounces{true}; + bool const bouncesZoom{true}; + bool const canCancelContentTouches{true}; + bool const centerContent{}; + bool const automaticallyAdjustContentInsets{}; + Float const decelerationRate{0.998}; + bool const directionalLockEnabled{}; + ScrollViewIndicatorStyle const indicatorStyle{}; + ScrollViewKeyboardDismissMode const keyboardDismissMode{}; + Float const maximumZoomScale{1.0}; + Float const minimumZoomScale{1.0}; + bool const scrollEnabled{true}; + bool const pagingEnabled{}; + bool const pinchGestureEnabled{true}; + bool const scrollsToTop{true}; + bool const showsHorizontalScrollIndicator{true}; + bool const showsVerticalScrollIndicator{true}; + Float const scrollEventThrottle{}; + Float const zoomScale{1.0}; + EdgeInsets const contentInset{}; + Point const contentOffset{}; + EdgeInsets const scrollIndicatorInsets{}; + Float const snapToInterval{}; + ScrollViewSnapToAlignment const snapToAlignment{}; #pragma mark - DebugStringConvertible diff --git a/ReactCommon/fabric/components/slider/BUCK b/ReactCommon/fabric/components/slider/BUCK index 41ce2c68985eb3..4f736a12d5160c 100644 --- a/ReactCommon/fabric/components/slider/BUCK +++ b/ReactCommon/fabric/components/slider/BUCK @@ -53,12 +53,10 @@ rn_xplat_cxx_library( fbandroid_headers = glob( ["platform/android/*.h"], ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( ["platform/android/*.cpp"], ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, ios_exported_headers = subdir_glob( @@ -74,6 +72,7 @@ rn_xplat_cxx_library( ios_srcs = glob( ["platform/ios/*.cpp"], ), + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/switch/BUCK b/ReactCommon/fabric/components/switch/BUCK index 8d12a6a070fa0d..c3a23b7811836c 100644 --- a/ReactCommon/fabric/components/switch/BUCK +++ b/ReactCommon/fabric/components/switch/BUCK @@ -44,11 +44,10 @@ rn_xplat_cxx_library( fbandroid_deps = [ react_native_target("jni/react/jni:jni"), ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/text/BUCK b/ReactCommon/fabric/components/text/BUCK index a1271eb16c66ec..f7d5ded2085661 100644 --- a/ReactCommon/fabric/components/text/BUCK +++ b/ReactCommon/fabric/components/text/BUCK @@ -43,11 +43,10 @@ rn_xplat_cxx_library( "-Wall", ], cxx_tests = [":tests"], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/text/basetext/BaseTextProps.h b/ReactCommon/fabric/components/text/basetext/BaseTextProps.h index ba9c7fe4058171..5554277f2daa1a 100644 --- a/ReactCommon/fabric/components/text/basetext/BaseTextProps.h +++ b/ReactCommon/fabric/components/text/basetext/BaseTextProps.h @@ -26,7 +26,7 @@ class BaseTextProps { #pragma mark - Props - const TextAttributes textAttributes{}; + TextAttributes textAttributes{}; #pragma mark - DebugStringConvertible (partially) diff --git a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h index 0e04b3c2b998b9..be95f4cacf1af6 100644 --- a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h +++ b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h @@ -57,6 +57,13 @@ class BaseTextShadowNode { ShadowNode const &parentNode, AttributedString &outAttributedString, Attachments &outAttachments); + + /** + * Returns a character used to measure empty strings in native platforms. + */ + inline static std::string getEmptyPlaceholder() { + return "I"; + } }; } // namespace react diff --git a/ReactCommon/fabric/components/text/paragraph/ParagraphComponentDescriptor.h b/ReactCommon/fabric/components/text/paragraph/ParagraphComponentDescriptor.h index d360c93d339d13..0ec89c568312f8 100644 --- a/ReactCommon/fabric/components/text/paragraph/ParagraphComponentDescriptor.h +++ b/ReactCommon/fabric/components/text/paragraph/ParagraphComponentDescriptor.h @@ -9,6 +9,7 @@ #include "ParagraphShadowNode.h" +#include #include #include #include @@ -30,6 +31,18 @@ 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 { ConcreteComponentDescriptor::adopt(shadowNode); diff --git a/ReactCommon/fabric/components/text/paragraph/ParagraphProps.cpp b/ReactCommon/fabric/components/text/paragraph/ParagraphProps.cpp index 672ad29d787c92..fc731707084b53 100644 --- a/ReactCommon/fabric/components/text/paragraph/ParagraphProps.cpp +++ b/ReactCommon/fabric/components/text/paragraph/ParagraphProps.cpp @@ -28,7 +28,14 @@ ParagraphProps::ParagraphProps( rawProps, "selectable", sourceProps.isSelectable, - {})){}; + {})) { + /* + * These props are applied to `View`, therefore they must not be a part of + * base text attributes. + */ + textAttributes.opacity = std::numeric_limits::quiet_NaN(); + textAttributes.backgroundColor = {}; +}; #pragma mark - DebugStringConvertible diff --git a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp index b93503a42edd58..3ead9c9a4bf897 100644 --- a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp +++ b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp @@ -120,13 +120,21 @@ Size ParagraphShadowNode::measure(LayoutConstraints layoutConstraints) const { auto content = getContentWithMeasuredAttachments(LayoutContext{}, layoutConstraints); - if (content.attributedString.isEmpty()) { - return layoutConstraints.clamp({0, 0}); + auto attributedString = content.attributedString; + if (attributedString.isEmpty()) { + // Note: `zero-width space` is insufficient in some cases (e.g. when we need + // to measure the "height" of the font). + // TODO T67606511: We will redefine the measurement of empty strings as part + // of T67606511 + auto string = BaseTextShadowNode::getEmptyPlaceholder(); + auto textAttributes = TextAttributes::defaultTextAttributes(); + textAttributes.apply(getConcreteProps().textAttributes); + attributedString.appendFragment({string, textAttributes, {}}); } return textLayoutManager_ ->measure( - AttributedStringBox{content.attributedString}, + AttributedStringBox{attributedString}, content.paragraphAttributes, layoutConstraints) .size; diff --git a/ReactCommon/fabric/components/textinput/BUCK b/ReactCommon/fabric/components/textinput/BUCK index 616d7c0468e780..49df25270c9066 100644 --- a/ReactCommon/fabric/components/textinput/BUCK +++ b/ReactCommon/fabric/components/textinput/BUCK @@ -40,11 +40,10 @@ rn_xplat_cxx_library( "-Wall", ], cxx_tests = [":tests"], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp b/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp index 0d90987f511cc3..1349f3f4e88654 100644 --- a/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp @@ -62,6 +62,8 @@ AttributedString AndroidTextInputShadowNode::getAttributedString() const { // single character in the string so that the measured height is greater // than zero. Otherwise, empty TextInputs with no placeholder don't // display at all. +// TODO T67606511: We will redefine the measurement of empty strings as part +// of T67606511 AttributedString AndroidTextInputShadowNode::getPlaceholderAttributedString() const { // Return placeholder text, since text and children are empty. @@ -70,7 +72,7 @@ AttributedString AndroidTextInputShadowNode::getPlaceholderAttributedString() fragment.string = getConcreteProps().placeholder; if (fragment.string.empty()) { - fragment.string = " "; + fragment.string = BaseTextShadowNode::getEmptyPlaceholder(); } auto textAttributes = TextAttributes::defaultTextAttributes(); diff --git a/ReactCommon/fabric/components/textinput/iostextinput/BUCK b/ReactCommon/fabric/components/textinput/iostextinput/BUCK index dfe4ccdf3df86e..f432474e0b2f9a 100644 --- a/ReactCommon/fabric/components/textinput/iostextinput/BUCK +++ b/ReactCommon/fabric/components/textinput/iostextinput/BUCK @@ -41,9 +41,9 @@ rn_xplat_cxx_library( ], cxx_tests = [":tests"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp b/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp index 5b65e24a325bea..fd70e306d73135 100644 --- a/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp +++ b/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp @@ -38,7 +38,11 @@ AttributedStringBox TextInputShadowNode::attributedStringBoxToMeasure() const { auto placeholder = getConcreteProps().placeholder; // Note: `zero-width space` is insufficient in some cases (e.g. when we need // to measure the "hight" of the font). - auto string = !placeholder.empty() ? placeholder : "I"; + // TODO T67606511: We will redefine the measurement of empty strings as part + // of T67606511 + auto string = !placeholder.empty() + ? placeholder + : BaseTextShadowNode::getEmptyPlaceholder(); auto textAttributes = getConcreteProps().getEffectiveTextAttributes(); attributedString.appendFragment({string, textAttributes, {}}); } diff --git a/ReactCommon/fabric/components/textinput/iostextinput/primitives.h b/ReactCommon/fabric/components/textinput/iostextinput/primitives.h index 1bd7caa9386bd3..f10fa2b42e368b 100644 --- a/ReactCommon/fabric/components/textinput/iostextinput/primitives.h +++ b/ReactCommon/fabric/components/textinput/iostextinput/primitives.h @@ -180,6 +180,12 @@ class TextInputTraits final { */ KeyboardType keyboardType{KeyboardType::Default}; + /* + * iOS & Android + * Default value: `true`. + */ + bool showSoftInputOnFocus{true}; + /* * Some values iOS- or Android-only (inherently particular-OS-specific) * Default value: `Default`. diff --git a/ReactCommon/fabric/components/textinput/iostextinput/propsConversions.h b/ReactCommon/fabric/components/textinput/iostextinput/propsConversions.h index 6c12a5716a15ab..6b5150d0aae095 100644 --- a/ReactCommon/fabric/components/textinput/iostextinput/propsConversions.h +++ b/ReactCommon/fabric/components/textinput/iostextinput/propsConversions.h @@ -88,6 +88,11 @@ static TextInputTraits convertRawProp( "keyboardType", sourceTraits.keyboardType, defaultTraits.keyboardType); + traits.showSoftInputOnFocus = convertRawProp( + rawProps, + "showSoftInputOnFocus", + sourceTraits.showSoftInputOnFocus, + defaultTraits.showSoftInputOnFocus); traits.returnKeyType = convertRawProp( rawProps, "returnKeyType", diff --git a/ReactCommon/fabric/components/unimplementedview/BUCK b/ReactCommon/fabric/components/unimplementedview/BUCK index aae773953d7f69..001f9607cdc532 100644 --- a/ReactCommon/fabric/components/unimplementedview/BUCK +++ b/ReactCommon/fabric/components/unimplementedview/BUCK @@ -36,8 +36,8 @@ rn_xplat_cxx_library( "-Wall", ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/components/view/BUCK b/ReactCommon/fabric/components/view/BUCK index eb8a63fe20375a..49b6f31659831c 100644 --- a/ReactCommon/fabric/components/view/BUCK +++ b/ReactCommon/fabric/components/view/BUCK @@ -41,11 +41,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/components/view/ViewComponentDescriptor.h b/ReactCommon/fabric/components/view/ViewComponentDescriptor.h index 34cb8107a357a8..006081daf3d2de 100644 --- a/ReactCommon/fabric/components/view/ViewComponentDescriptor.h +++ b/ReactCommon/fabric/components/view/ViewComponentDescriptor.h @@ -9,11 +9,30 @@ #include #include +#include "ViewProps.h" +#include "ViewPropsInterpolation.h" namespace facebook { namespace react { -using ViewComponentDescriptor = ConcreteComponentDescriptor; +class ViewComponentDescriptor + : public ConcreteComponentDescriptor { + 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 } // namespace facebook diff --git a/ReactCommon/fabric/components/view/ViewPropsInterpolation.h b/ReactCommon/fabric/components/view/ViewPropsInterpolation.h new file mode 100644 index 00000000000000..0470d696d514cc --- /dev/null +++ b/ReactCommon/fabric/components/view/ViewPropsInterpolation.h @@ -0,0 +1,58 @@ +/* + * 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 "ViewProps.h" + +namespace facebook { +namespace react { + +/** + * Given animation progress, old props, new props, and an "interpolated" shared + * props struct, this will mutate the "interpolated" struct in-place to give it + * values interpolated between the old and new props. + */ +static inline void interpolateViewProps( + Float animationProgress, + const SharedProps &oldPropsShared, + const SharedProps &newPropsShared, + SharedProps &interpolatedPropsShared) { + ViewProps const *oldViewProps = + dynamic_cast(oldPropsShared.get()); + ViewProps const *newViewProps = + dynamic_cast(newPropsShared.get()); + ViewProps *interpolatedProps = const_cast( + dynamic_cast(interpolatedPropsShared.get())); + + assert( + oldViewProps != nullptr && newViewProps != nullptr && + interpolatedProps != nullptr); + + interpolatedProps->opacity = oldViewProps->opacity + + (newViewProps->opacity - oldViewProps->opacity) * animationProgress; + + interpolatedProps->transform = Transform::Interpolate( + animationProgress, oldViewProps->transform, newViewProps->transform); + + // Android uses RawProps, not props, to update props on the platform... + // Since interpolated props don't interpolate at all using RawProps, we need + // to "re-hydrate" raw props after interpolating. This is what actually gets + // sent to the mounting layer. This is a temporary hack, only for platforms + // that use RawProps/folly::dynamic instead of concrete props on the + // mounting layer. Once we can remove this, we should change `rawProps` to + // be const again. +#ifdef ANDROID + interpolatedProps->rawProps["opacity"] = interpolatedProps->opacity; + + interpolatedProps->rawProps["transform"] = + (folly::dynamic)interpolatedProps->transform; +#endif +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/view/ViewShadowNode.cpp b/ReactCommon/fabric/components/view/ViewShadowNode.cpp index 6f58a7a0b4ad5c..b503bb2986b7cb 100644 --- a/ReactCommon/fabric/components/view/ViewShadowNode.cpp +++ b/ReactCommon/fabric/components/view/ViewShadowNode.cpp @@ -43,8 +43,9 @@ void ViewShadowNode::initialize() noexcept { viewProps.pointerEvents == PointerEventsMode::None || !viewProps.nativeId.empty() || viewProps.accessible || viewProps.opacity != 1.0 || viewProps.transform != Transform{} || - viewProps.zIndex != 0 || viewProps.getClipsContentToBounds() || - viewProps.yogaStyle.positionType() == YGPositionTypeAbsolute || + (viewProps.zIndex != 0 && + viewProps.yogaStyle.positionType() == YGPositionTypeAbsolute) || + viewProps.getClipsContentToBounds() || isColorMeaningful(viewProps.shadowColor); bool formsView = isColorMeaningful(viewProps.backgroundColor) || diff --git a/ReactCommon/fabric/components/view/conversions.h b/ReactCommon/fabric/components/view/conversions.h index f7ad89b65187ed..0ac41614dd0dfc 100644 --- a/ReactCommon/fabric/components/view/conversions.h +++ b/ReactCommon/fabric/components/view/conversions.h @@ -261,7 +261,7 @@ inline void fromRawValue(const RawValue &value, YGAlign &result) { result = YGAlignBaseline; return; } - if (stringValue == "between") { + if (stringValue == "space-between") { result = YGAlignSpaceBetween; return; } diff --git a/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.cpp b/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.cpp index 04c48fda480595..1a9982b2cf50e1 100644 --- a/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.cpp +++ b/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.cpp @@ -73,6 +73,7 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode( &initializeYogaConfig(yogaConfig_)) { yogaNode_.setContext(this); yogaNode_.setOwner(nullptr); + updateYogaChildrenOwnersIfNeeded(); // Yoga node must inherit dirty flag. assert( @@ -223,6 +224,14 @@ bool YogaLayoutableShadowNode::doesOwn( return child.yogaNode_.getOwner() == &yogaNode_; } +void YogaLayoutableShadowNode::updateYogaChildrenOwnersIfNeeded() { + for (auto &childYogaNode : yogaNode_.getChildren()) { + if (childYogaNode->getOwner() == &yogaNode_) { + childYogaNode->setOwner(reinterpret_cast(0xBADC0FFEE0DDF00D)); + } + } +} + void YogaLayoutableShadowNode::updateYogaChildren() { if (getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)) { return; @@ -597,7 +606,7 @@ void YogaLayoutableShadowNode::ensureYogaChildrenOwnersConsistency() const { // The owner might be not equal to the `yogaNode_` though. auto &yogaChildren = yogaNode_.getChildren(); - if (yogaChildren.size() > 0) { + if (!yogaChildren.empty()) { auto owner = yogaChildren.at(0)->getOwner(); for (auto const &child : yogaChildren) { assert(child->getOwner() == owner); @@ -617,7 +626,7 @@ void YogaLayoutableShadowNode::ensureYogaChildrenLookFine() const { for (auto const &yogaChild : yogaChildren) { assert(yogaChild->getContext()); assert(yogaChild->getChildren().size() < 16384); - if (yogaChild->getChildren().size() > 0) { + if (!yogaChild->getChildren().empty()) { assert(!yogaChild->hasMeasureFunc()); } } @@ -635,7 +644,7 @@ void YogaLayoutableShadowNode::ensureYogaChildrenAlighment() const { auto &children = getChildren(); if (getTraits().check(ShadowNodeTraits::Trait::LeafYogaNode)) { - assert(yogaChildren.size() == 0); + assert(yogaChildren.empty()); return; } diff --git a/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h b/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h index e9ea29cd477e87..59148e333b80f3 100644 --- a/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h +++ b/ReactCommon/fabric/components/view/yoga/YogaLayoutableShadowNode.h @@ -100,6 +100,16 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode { mutable YGNode yogaNode_; private: + /* + * Goes over `yogaNode_.getChildren()` and in case child's owner is + * equal to address of `yogaNode_`, it sets child's owner address + * to `0xBADC0FFEE0DDF00D`. This is magic constant, the intention + * is to make debugging easier when the address pops up in debugger. + * This prevents ABA problem where child yoga node goes from owned -> unowned + * -> back to owned because its parent is allocated at the same address. + */ + void updateYogaChildrenOwnersIfNeeded(); + /* * Return true if child's yogaNode's owner is this->yogaNode_. Otherwise * returns false. diff --git a/ReactCommon/fabric/core/BUCK b/ReactCommon/fabric/core/BUCK index 5930a38025e754..948f56912cb165 100644 --- a/ReactCommon/fabric/core/BUCK +++ b/ReactCommon/fabric/core/BUCK @@ -44,11 +44,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/core/componentdescriptor/ComponentDescriptor.h b/ReactCommon/fabric/core/componentdescriptor/ComponentDescriptor.h index c9d2feb75af004..9d47425bc9b2c4 100644 --- a/ReactCommon/fabric/core/componentdescriptor/ComponentDescriptor.h +++ b/ReactCommon/fabric/core/componentdescriptor/ComponentDescriptor.h @@ -104,6 +104,15 @@ class ComponentDescriptor { const SharedProps &props, const RawProps &rawProps) const = 0; + /* + * Creates a new `Props` of a particular type with all values interpolated + * between `props` and `newProps`. + */ + virtual SharedProps interpolateProps( + float animationProgress, + const SharedProps &props, + const SharedProps &newProps) const = 0; + /* * Create an initial State object that represents (and contains) an initial * State's data which can be constructed based on initial Props. diff --git a/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h b/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h index 43fbeaf40cf3ae..e987de792eb575 100644 --- a/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h +++ b/ReactCommon/fabric/core/componentdescriptor/ConcreteComponentDescriptor.h @@ -122,6 +122,14 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { return ShadowNodeT::Props(rawProps, props); }; + virtual SharedProps interpolateProps( + float animationProgress, + const SharedProps &props, + const SharedProps &newProps) const override { + // By default, this does nothing. + return cloneProps(newProps, {}); + }; + virtual State::Shared createInitialState( ShadowNodeFragment const &fragment, ShadowNodeFamily::Shared const &family) const override { diff --git a/ReactCommon/fabric/core/events/BatchedEventQueue.cpp b/ReactCommon/fabric/core/events/BatchedEventQueue.cpp index 49758f22bb18de..baf25201294dd4 100644 --- a/ReactCommon/fabric/core/events/BatchedEventQueue.cpp +++ b/ReactCommon/fabric/core/events/BatchedEventQueue.cpp @@ -6,6 +6,7 @@ */ #include "BatchedEventQueue.h" +#include namespace facebook { namespace react { @@ -16,5 +17,23 @@ void BatchedEventQueue::onEnqueue() const { eventBeat_->request(); } +void BatchedEventQueue::enqueueUniqueEvent(const RawEvent &rawEvent) const { + { + std::lock_guard lock(queueMutex_); + auto const position = std::find_if( + eventQueue_.begin(), eventQueue_.end(), [&rawEvent](auto const &event) { + return event.type == rawEvent.type && + event.eventTarget == rawEvent.eventTarget; + }); + if (position != eventQueue_.end()) { + eventQueue_.erase(position); + } + + eventQueue_.push_back(rawEvent); + } + + onEnqueue(); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/core/events/BatchedEventQueue.h b/ReactCommon/fabric/core/events/BatchedEventQueue.h index f241f24a176d27..84bcb0ff39b8ab 100644 --- a/ReactCommon/fabric/core/events/BatchedEventQueue.h +++ b/ReactCommon/fabric/core/events/BatchedEventQueue.h @@ -21,6 +21,14 @@ class BatchedEventQueue final : public EventQueue { using EventQueue::EventQueue; void onEnqueue() const override; + + /* + * Enqueues and (probably later) dispatch a given event. + * Deletes previous RawEvent of the same type and with same target + * from the queue. + * Can be called on any thread. + */ + void enqueueUniqueEvent(const RawEvent &rawEvent) const; }; } // namespace react diff --git a/ReactCommon/fabric/core/events/EventDispatcher.cpp b/ReactCommon/fabric/core/events/EventDispatcher.cpp index dd08e2330ca305..5ccd35e569122e 100644 --- a/ReactCommon/fabric/core/events/EventDispatcher.cpp +++ b/ReactCommon/fabric/core/events/EventDispatcher.cpp @@ -21,27 +21,23 @@ EventDispatcher::EventDispatcher( StatePipe const &statePipe, EventBeat::Factory const &synchonousEventBeatFactory, EventBeat::Factory const &asynchonousEventBeatFactory, - EventBeat::SharedOwnerBox const &ownerBox) { - // Synchronous/Unbatched - eventQueues_[(int)EventPriority::SynchronousUnbatched] = - std::make_unique( - eventPipe, statePipe, synchonousEventBeatFactory(ownerBox)); - - // Synchronous/Batched - eventQueues_[(int)EventPriority::SynchronousBatched] = - std::make_unique( - eventPipe, statePipe, synchonousEventBeatFactory(ownerBox)); - - // Asynchronous/Unbatched - eventQueues_[(int)EventPriority::AsynchronousUnbatched] = - std::make_unique( - eventPipe, statePipe, asynchonousEventBeatFactory(ownerBox)); - - // Asynchronous/Batched - eventQueues_[(int)EventPriority::AsynchronousBatched] = - std::make_unique( - eventPipe, statePipe, asynchonousEventBeatFactory(ownerBox)); -} + EventBeat::SharedOwnerBox const &ownerBox) + : synchronousUnbatchedQueue_(std::make_unique( + eventPipe, + statePipe, + synchonousEventBeatFactory(ownerBox))), + synchronousBatchedQueue_(std::make_unique( + eventPipe, + statePipe, + synchonousEventBeatFactory(ownerBox))), + asynchronousUnbatchedQueue_(std::make_unique( + eventPipe, + statePipe, + asynchonousEventBeatFactory(ownerBox))), + asynchronousBatchedQueue_(std::make_unique( + eventPipe, + statePipe, + asynchonousEventBeatFactory(ownerBox))) {} void EventDispatcher::dispatchEvent( RawEvent const &rawEvent, @@ -55,8 +51,21 @@ void EventDispatcher::dispatchStateUpdate( getEventQueue(priority).enqueueStateUpdate(std::move(stateUpdate)); } +void EventDispatcher::dispatchUniqueEvent(RawEvent const &rawEvent) const { + asynchronousBatchedQueue_->enqueueUniqueEvent(rawEvent); +} + const EventQueue &EventDispatcher::getEventQueue(EventPriority priority) const { - return *eventQueues_[(int)priority]; + switch (priority) { + case EventPriority::SynchronousUnbatched: + return *synchronousUnbatchedQueue_; + case EventPriority::SynchronousBatched: + return *synchronousBatchedQueue_; + case EventPriority::AsynchronousUnbatched: + return *asynchronousUnbatchedQueue_; + case EventPriority::AsynchronousBatched: + return *asynchronousBatchedQueue_; + } } } // namespace react diff --git a/ReactCommon/fabric/core/events/EventDispatcher.h b/ReactCommon/fabric/core/events/EventDispatcher.h index 47a1d826ad3580..f9f161bd50e75d 100644 --- a/ReactCommon/fabric/core/events/EventDispatcher.h +++ b/ReactCommon/fabric/core/events/EventDispatcher.h @@ -10,12 +10,13 @@ #include #include +#include #include #include #include -#include #include #include +#include namespace facebook { namespace react { @@ -43,6 +44,13 @@ class EventDispatcher { */ void dispatchEvent(RawEvent const &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; + /* * Dispatches a state update with given priority. */ @@ -52,7 +60,10 @@ class EventDispatcher { private: EventQueue const &getEventQueue(EventPriority priority) const; - std::array, 4> eventQueues_; + std::unique_ptr synchronousUnbatchedQueue_; + std::unique_ptr synchronousBatchedQueue_; + std::unique_ptr asynchronousUnbatchedQueue_; + std::unique_ptr asynchronousBatchedQueue_; }; } // namespace react diff --git a/ReactCommon/fabric/core/events/EventEmitter.cpp b/ReactCommon/fabric/core/events/EventEmitter.cpp index 368bdad8d31eb3..007911fcc593c2 100644 --- a/ReactCommon/fabric/core/events/EventEmitter.cpp +++ b/ReactCommon/fabric/core/events/EventEmitter.cpp @@ -77,6 +77,20 @@ void EventEmitter::dispatchEvent( priority); } +void EventEmitter::dispatchUniqueEvent( + const std::string &type, + const ValueFactory &payloadFactory) const { + SystraceSection s("EventEmitter::dispatchUniqueEvent"); + + auto eventDispatcher = eventDispatcher_.lock(); + if (!eventDispatcher) { + return; + } + + eventDispatcher->dispatchUniqueEvent( + RawEvent(normalizeEventType(type), payloadFactory, eventTarget_)); +} + void EventEmitter::setEnabled(bool enabled) const { enableCounter_ += enabled ? 1 : -1; diff --git a/ReactCommon/fabric/core/events/EventEmitter.h b/ReactCommon/fabric/core/events/EventEmitter.h index 91db82ed3a611f..14475e01a019d3 100644 --- a/ReactCommon/fabric/core/events/EventEmitter.h +++ b/ReactCommon/fabric/core/events/EventEmitter.h @@ -78,6 +78,11 @@ class EventEmitter { const folly::dynamic &payload, const EventPriority &priority = EventPriority::AsynchronousBatched) const; + void dispatchUniqueEvent( + const std::string &type, + const ValueFactory &payloadFactory = + EventEmitter::defaultPayloadFactory()) const; + private: void toggleEventTargetOwnership_() const; diff --git a/ReactCommon/fabric/core/events/EventQueue.cpp b/ReactCommon/fabric/core/events/EventQueue.cpp index 1d1aa9e6ea0165..2861d7a14f05ec 100644 --- a/ReactCommon/fabric/core/events/EventQueue.cpp +++ b/ReactCommon/fabric/core/events/EventQueue.cpp @@ -96,7 +96,7 @@ void EventQueue::flushStateUpdates() const { { std::lock_guard lock(queueMutex_); - if (stateUpdateQueue_.size() == 0) { + if (stateUpdateQueue_.empty()) { return; } diff --git a/ReactCommon/fabric/core/events/RawEvent.h b/ReactCommon/fabric/core/events/RawEvent.h index e3985cef83b209..8ac0800b586d14 100644 --- a/ReactCommon/fabric/core/events/RawEvent.h +++ b/ReactCommon/fabric/core/events/RawEvent.h @@ -26,9 +26,9 @@ class RawEvent { ValueFactory payloadFactory, SharedEventTarget eventTarget); - const std::string type; - const ValueFactory payloadFactory; - const SharedEventTarget eventTarget; + std::string type; + ValueFactory payloadFactory; + SharedEventTarget eventTarget; }; } // namespace react diff --git a/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp b/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp index 4ae44ed720e3cc..c6f391eccef261 100644 --- a/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp +++ b/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp @@ -17,54 +17,95 @@ namespace facebook { namespace react { -/* - * `shadowNode` might not be the newest revision of `ShadowNodeFamily`. - * This function looks at `parentNode`'s children and finds one that belongs - * to the same family as `shadowNode`. - */ -static ShadowNode const *findNewestChildInParent( - ShadowNode const &parentNode, - ShadowNode const &shadowNode) { - for (auto const &child : parentNode.getChildren()) { - if (ShadowNode::sameFamily(*child, shadowNode)) { - return child.get(); +LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics( + ShadowNodeFamily const &descendantNodeFamily, + LayoutableShadowNode const &ancestorNode, + LayoutInspectingPolicy policy) { + if (&descendantNodeFamily == &ancestorNode.getFamily()) { + // Layout metrics of a node computed relatively to the same node are equal + // to `transform`-ed layout metrics of the node with zero `origin`. + auto layoutMetrics = ancestorNode.getLayoutMetrics(); + if (policy.includeTransform) { + layoutMetrics.frame = layoutMetrics.frame * ancestorNode.getTransform(); } + layoutMetrics.frame.origin = {0, 0}; + return layoutMetrics; } - return nullptr; -} -static LayoutMetrics calculateOffsetForLayoutMetrics( - LayoutMetrics layoutMetrics, - ShadowNode::AncestorList const &ancestors, - LayoutableShadowNode::LayoutInspectingPolicy const &policy) { - // `AncestorList` starts from the given ancestor node and ends with the parent - // node. We iterate from parent node (reverse iteration) and stop before the - // given ancestor (rend() - 1). - for (auto it = ancestors.rbegin(); it != ancestors.rend() - 1; ++it) { - auto ¤tShadowNode = it->first.get(); - - if (currentShadowNode.getTraits().check( - ShadowNodeTraits::Trait::RootNodeKind)) { + auto ancestors = descendantNodeFamily.getAncestors(ancestorNode); + + if (ancestors.size() == 0) { + // Specified nodes do not form an ancestor-descender relationship + // in the same tree. Aborting. + return EmptyLayoutMetrics; + } + + // Step 1. + // Creating a list of nodes that form a chain from the descender node to + // ancestor node inclusively. + auto shadowNodeList = better::small_vector{}; + + // Finding the measured node. + // The last element in the `AncestorList` is a pair of a parent of the node + // and an index of this node in the parent's children list. + auto &pair = ancestors.at(ancestors.size() - 1); + auto descendantNode = pair.first.get().getChildren().at(pair.second).get(); + + // Putting the node inside the list. + // Even if this is a node with a `RootNodeKind` trait, we don't treat it as + // root because we measure it from an outside tree perspective. + shadowNodeList.push_back(descendantNode); + + for (auto it = ancestors.rbegin(); it != ancestors.rend(); it++) { + auto &shadowNode = it->first.get(); + + shadowNodeList.push_back(&shadowNode); + + if (shadowNode.getTraits().check(ShadowNodeTraits::Trait::RootNodeKind)) { + // If this is a node with a `RootNodeKind` trait, we need to stop right + // there. break; } + } + + // Step 2. + // Computing the initial size of the measured node. + auto descendantLayoutableNode = + traitCast(descendantNode); - auto layoutableCurrentShadowNode = - dynamic_cast(¤tShadowNode); + if (!descendantLayoutableNode) { + return EmptyLayoutMetrics; + } - if (!layoutableCurrentShadowNode) { + auto layoutMetrics = descendantLayoutableNode->getLayoutMetrics(); + auto &resultFrame = layoutMetrics.frame; + resultFrame.origin = {0, 0}; + + // Step 3. + // Iterating on a list of nodes computing compound offset. + auto size = shadowNodeList.size(); + for (int i = 0; i < size; i++) { + auto currentShadowNode = + traitCast(shadowNodeList.at(i)); + + if (!currentShadowNode) { return EmptyLayoutMetrics; } - auto frame = layoutableCurrentShadowNode->getLayoutMetrics().frame; + auto currentFrame = currentShadowNode->getLayoutMetrics().frame; + if (i == size - 1) { + // If it's the last element, its origin is irrelevant. + currentFrame.origin = {0, 0}; + } if (policy.includeTransform) { - layoutMetrics.frame.size = layoutMetrics.frame.size * - layoutableCurrentShadowNode->getTransform(); - frame = frame * layoutableCurrentShadowNode->getTransform(); + resultFrame.size = resultFrame.size * currentShadowNode->getTransform(); + currentFrame = currentFrame * currentShadowNode->getTransform(); } - layoutMetrics.frame.origin += frame.origin; + resultFrame.origin += currentFrame.origin; } + return layoutMetrics; } @@ -109,37 +150,8 @@ Transform LayoutableShadowNode::getTransform() const { LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics( LayoutableShadowNode const &ancestorLayoutableShadowNode, LayoutInspectingPolicy policy) const { - auto &ancestorShadowNode = - dynamic_cast(ancestorLayoutableShadowNode); - auto &shadowNode = dynamic_cast(*this); - - if (ShadowNode::sameFamily(shadowNode, ancestorShadowNode)) { - auto layoutMetrics = getLayoutMetrics(); - layoutMetrics.frame.origin = {0, 0}; - return layoutMetrics; - } - - auto ancestors = shadowNode.getFamily().getAncestors(ancestorShadowNode); - - if (ancestors.size() == 0) { - return EmptyLayoutMetrics; - } - - auto newestChild = - findNewestChildInParent(ancestors.rbegin()->first.get(), shadowNode); - - if (!newestChild) { - return EmptyLayoutMetrics; - } - - auto layoutableNewestChild = - dynamic_cast(newestChild); - auto layoutMetrics = layoutableNewestChild->getLayoutMetrics(); - if (policy.includeTransform) { - layoutMetrics.frame = - layoutMetrics.frame * layoutableNewestChild->getTransform(); - } - return calculateOffsetForLayoutMetrics(layoutMetrics, ancestors, policy); + return computeRelativeLayoutMetrics( + getFamily(), ancestorLayoutableShadowNode, policy); } LayoutableShadowNode::UnsharedList diff --git a/ReactCommon/fabric/core/layout/LayoutableShadowNode.h b/ReactCommon/fabric/core/layout/LayoutableShadowNode.h index 78601fa5e221c0..d94566390c443b 100644 --- a/ReactCommon/fabric/core/layout/LayoutableShadowNode.h +++ b/ReactCommon/fabric/core/layout/LayoutableShadowNode.h @@ -51,6 +51,17 @@ class LayoutableShadowNode : public ShadowNode { using UnsharedList = better:: small_vector; + /* + * Returns layout metrics of a node represented as `descendantNodeFamily` + * computed relatively to given `ancestorNode`. Returns `EmptyLayoutMetrics` + * if the nodes don't form an ancestor-descender relationship in the same + * tree. + */ + static LayoutMetrics computeRelativeLayoutMetrics( + ShadowNodeFamily const &descendantNodeFamily, + LayoutableShadowNode const &ancestorNode, + LayoutInspectingPolicy policy); + /* * Performs layout of the tree starting from this node. Usually is being * called on the root node. @@ -99,6 +110,14 @@ class LayoutableShadowNode : public ShadowNode { */ virtual Transform getTransform() 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. */ diff --git a/ReactCommon/fabric/core/primitives/RawValue.h b/ReactCommon/fabric/core/primitives/RawValue.h index 67f6ac05dcc252..75c2e9527a7cbc 100644 --- a/ReactCommon/fabric/core/primitives/RawValue.h +++ b/ReactCommon/fabric/core/primitives/RawValue.h @@ -64,6 +64,7 @@ class RawValue { private: friend class RawProps; friend class RawPropsParser; + friend class UIManagerBinding; /* * Arbitrary constructors are private only for RawProps and internal usage. @@ -73,9 +74,9 @@ class RawValue { RawValue(folly::dynamic &&dynamic) noexcept : dynamic_(std::move(dynamic)){}; /* - * Copy constructor and copy assignment operator are private and only for - * internal use. Basically, it's implementation details. Other particular - * implementations of the `RawValue` interface may not have them. + * Copy constructor and copy assignment operator would be private and only for + * internal use, but it's needed for user-code that does `auto val = + * (better::map)rawVal;` */ RawValue(RawValue const &other) noexcept : dynamic_(other.dynamic_) {} diff --git a/ReactCommon/fabric/core/shadownode/Props.h b/ReactCommon/fabric/core/shadownode/Props.h index 340041448c790a..6e0f46b3785ee2 100644 --- a/ReactCommon/fabric/core/shadownode/Props.h +++ b/ReactCommon/fabric/core/shadownode/Props.h @@ -43,7 +43,7 @@ class Props : public virtual Sealable, public virtual DebugStringConvertible { int const revision{0}; #ifdef ANDROID - folly::dynamic const rawProps = folly::dynamic::object(); + folly::dynamic rawProps = folly::dynamic::object(); #endif }; diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.cpp b/ReactCommon/fabric/core/shadownode/ShadowNode.cpp index f54fedcf22b073..9f3618ddf466f3 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.cpp +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.cpp @@ -254,7 +254,7 @@ ShadowNode::Unshared ShadowNode::cloneTree( callback) const { auto ancestors = shadowNodeFamily.getAncestors(*this); - if (ancestors.size() == 0) { + if (ancestors.empty()) { return ShadowNode::Unshared{nullptr}; } diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index 468670698f7778..30710ec0e468e4 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -90,6 +90,12 @@ class ShadowNode : public Sealable, public DebugStringConvertible { const ShadowNode &sourceShadowNode, const ShadowNodeFragment &fragment); + /* + * Not copyable. + */ + ShadowNode(ShadowNode const &shadowNode) noexcept = delete; + ShadowNode &operator=(ShadowNode const &other) noexcept = delete; + virtual ~ShadowNode() = default; /* diff --git a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp index 9c13ae700f13d0..7d59e94b4534af 100644 --- a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp +++ b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp @@ -113,6 +113,8 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetrics) { // 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}. // D19447900 has more about the issue. + EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 100); + EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 200); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20); } @@ -131,9 +133,10 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedNode) { EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 35); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 70); - EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 50); EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 100); + + nodeAA_->_transform = Transform::Identity(); } TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) { @@ -159,6 +162,8 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) { EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 25); EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 25); + + nodeAAA_->_transform = Transform::Identity(); } TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode) { @@ -175,6 +180,23 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode) { EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 200); } +TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameTransformedNode) { + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 20}; + layoutMetrics.frame.size = {100, 200}; + nodeA_->setLayoutMetrics(layoutMetrics); + nodeA_->_transform = Transform::Scale(2, 2, 1); + + auto relativeLayoutMetrics = nodeA_->getRelativeLayoutMetrics(*nodeA_, {}); + + EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0); + EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 0); + EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 200); + EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 400); + + nodeA_->_transform = Transform::Identity(); +} + TEST_F(LayoutableShadowNodeTest, relativeLayourMetricsOnClonedNode) { // B is cloned and mutated. auto nodeBRevision2 = std::static_pointer_cast( @@ -192,15 +214,21 @@ TEST_F(LayoutableShadowNodeTest, relativeLayourMetricsOnClonedNode) { EXPECT_EQ(newRelativeLayoutMetrics.frame.size.height, 600); } -TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode2) { +TEST_F( + LayoutableShadowNodeTest, + relativeLayoutMetricsOnNodesCrossingRootKindNode) { auto layoutMetrics = EmptyLayoutMetrics; nodeA_->setLayoutMetrics(layoutMetrics); layoutMetrics.frame.origin = {10, 10}; + // nodeAA_ is a RootKindNode. Please check + // ShadowNodeTraits::Traits::RootNodeKind for details. nodeAA_->setLayoutMetrics(layoutMetrics); nodeAAA_->setLayoutMetrics(layoutMetrics); auto relativeLayoutMetrics = nodeAAA_->getRelativeLayoutMetrics(*nodeA_, {}); + // relativeLayoutMetrics do not include offsset of nodeAA_ because it is a + // RootKindNode. EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10); EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 10); } diff --git a/ReactCommon/fabric/debug/BUCK b/ReactCommon/fabric/debug/BUCK index 9971b09cb503e3..af8ed5594dbc08 100644 --- a/ReactCommon/fabric/debug/BUCK +++ b/ReactCommon/fabric/debug/BUCK @@ -39,11 +39,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/element/BUCK b/ReactCommon/fabric/element/BUCK index f7fce793547bdd..c5fc20db81a342 100644 --- a/ReactCommon/fabric/element/BUCK +++ b/ReactCommon/fabric/element/BUCK @@ -38,9 +38,9 @@ rn_xplat_cxx_library( "-Wall", ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/graphics/BUCK b/ReactCommon/fabric/graphics/BUCK index bcedde153577c2..a495bd817ac30c 100644 --- a/ReactCommon/fabric/graphics/BUCK +++ b/ReactCommon/fabric/graphics/BUCK @@ -56,14 +56,12 @@ rn_xplat_cxx_library( ], prefix = "react/graphics", ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( [ "platform/cxx/**/*.cpp", ], ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, ios_deps = [ @@ -87,6 +85,7 @@ rn_xplat_cxx_library( "platform/ios/**/*.mm", ], ), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/graphics/Quaternion.h b/ReactCommon/fabric/graphics/Quaternion.h new file mode 100644 index 00000000000000..4233a2c6cb4409 --- /dev/null +++ b/ReactCommon/fabric/graphics/Quaternion.h @@ -0,0 +1,208 @@ +/* + * Portions 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 + +// The following is a modified, stripped-down version of the Quaternion class +// by Frank Astier. Copyright notice below. +// The original has many, many more features, and has been stripped down +// to support the exact data-structures and use-cases we need for React Native. + +/** + * The MIT License (MIT) + * + * Copyright (c) 2015 Frank Astier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace facebook { +namespace react { + +template +class Quaternion { + public: + /** + * Copy constructor. + */ + Quaternion(const Quaternion &y) : a_(y.a_), b_(y.b_), c_(y.c_), d_(y.d_) {} + + Quaternion(T a, T b, T c, T d) : a_(a), b_(b), c_(c), d_(d) {} + + static Quaternion fromRotationMatrix(std::array const &rm) { + T t = rm[0 * 4 + 0] + rm[1 * 4 + 1] + rm[2 * 4 + 2]; + if (t > 0) { + T s = (T)0.5 / std::sqrt(t + 1); + return {(T)0.25 / s, + (rm[2 * 4 + 1] - rm[1 * 4 + 2]) * s, + (rm[0 * 4 + 2] - rm[2 * 4 + 0]) * s, + (rm[1 * 4 + 0] - rm[0 * 4 + 1]) * s}; + } else if (rm[0 * 4 + 0] > rm[1 * 4 + 1] && rm[0 * 4 + 0] > rm[2 * 4 + 2]) { + T s = (T)2.0 * + std::sqrt( + 1.0 + rm[0 * 4 + 0] - rm[1 * 4 + 1] - rm[2 * 4 + 2]); // S=4*qx + return {(rm[2 * 4 + 1] - rm[1 * 4 + 2]) / s, + (T)0.25 * s, + (rm[0 * 4 + 1] + rm[1 * 4 + 0]) / s, + (rm[0 * 4 + 2] + rm[2 * 4 + 0]) / s}; + } else if (rm[1 * 4 + 1] > rm[2 * 4 + 2]) { + T s = (T)2.0 * + std::sqrt( + 1.0 + rm[1 * 4 + 1] - rm[0 * 4 + 0] - rm[2 * 4 + 2]); // S=4*qy + return {(rm[0 * 4 + 2] - rm[2 * 4 + 0]) / s, + (rm[0 * 4 + 1] + rm[1 * 4 + 0]) / s, + (T)0.25 * s, + (rm[1 * 4 + 2] + rm[2 * 4 + 1]) / s}; + } else { + T s = (T)2.0 * + std::sqrt( + 1.0 + rm[2 * 4 + 2] - rm[0 * 4 + 0] - rm[1 * 4 + 1]); // S=4*qz + return {(rm[1 * 4 + 0] - rm[0 * 4 + 1]) / s, + (rm[0 * 4 + 2] + rm[2 * 4 + 0]) / s, + (rm[1 * 4 + 2] + rm[2 * 4 + 1]) / s, + (T)0.25 * s}; + } + } + + /** + * Returns a 3D, 4x4 rotation matrix. + * This is the "homogeneous" expression to convert to a rotation matrix, + * which works even when the Quaternion is not a unit Quaternion. + */ + inline std::array toRotationMatrix4x4() { + T a2 = a_ * a_, b2 = b_ * b_, c2 = c_ * c_, d2 = d_ * d_; + T ab = a_ * b_, ac = a_ * c_, ad = a_ * d_; + T bc = b_ * c_, bd = b_ * d_; + T cd = c_ * d_; + return {a2 + b2 - c2 - d2, + 2 * (bc - ad), + 2 * (bd + ac), + 0, + 2 * (bc + ad), + a2 - b2 + c2 - d2, + 2 * (cd - ab), + 0, + 2 * (bd - ac), + 2 * (cd + ab), + a2 - b2 - c2 + d2, + 0, + 0, + 0, + 0, + 1}; + } + + inline Quaternion normalize() const { + assert(abs() > 0); // or this is not normalizable + T factor = abs(); + return *this / (factor != 0 ? factor : 1); + } + + inline T dot(const Quaternion &other) { + return a_ * other.a_ + b_ * other.b_ + c_ * other.c_ + d_ * other.d_; + } + + /** + * The square of the norm of the Quaternion. + * (The square is sometimes useful, and it avoids paying for a sqrt). + */ + inline T norm_squared() const { + return a_ * a_ + b_ * b_ + c_ * c_ + d_ * d_; + } + + /** + * The norm of the Quaternion (the l2 norm). + */ + inline T abs() const { + return std::sqrt(norm_squared()); + } + + inline Quaternion operator/=(T y) { + a_ /= y; + b_ /= y; + c_ /= y; + d_ /= y; + return *this; + } + + inline Quaternion operator*=(T y) { + a_ *= y; + b_ *= y; + c_ *= y; + d_ *= y; + return *this; + } + + inline Quaternion operator+=(Quaternion const &other) { + a_ += other.a_; + b_ += other.b_; + c_ += other.c_; + d_ += other.d_; + return *this; + } + + inline Quaternion operator-=(Quaternion const &other) { + a_ -= other.a_; + b_ -= other.b_; + c_ -= other.c_; + d_ -= other.d_; + return *this; + } + + private: + T a_; // AKA w, qw + T b_; // AKA x, qx + T c_; // AKA y, qy + T d_; // AKA z, qz +}; + +template +inline Quaternion operator/(Quaternion const &lhs, T rhs) { + return Quaternion(lhs) /= rhs; +} + +template +inline Quaternion operator*(Quaternion const &lhs, T rhs) { + return Quaternion(lhs) *= rhs; +} + +template +inline Quaternion operator+( + Quaternion const &lhs, + Quaternion const &rhs) { + return Quaternion(lhs) += rhs; +} + +template +inline Quaternion operator-( + Quaternion const &lhs, + Quaternion const &rhs) { + return Quaternion(lhs) -= rhs; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/graphics/Transform.cpp b/ReactCommon/fabric/graphics/Transform.cpp index 7293e03312b855..f55721c3fc6be8 100644 --- a/ReactCommon/fabric/graphics/Transform.cpp +++ b/ReactCommon/fabric/graphics/Transform.cpp @@ -7,18 +7,34 @@ #include "Transform.h" +#include #include +#include + namespace facebook { namespace react { +#ifdef RN_DEBUG_STRING_CONVERTIBLE +void Transform::print(Transform const &t, std::string prefix) { + LOG(ERROR) << prefix << "[ " << t.matrix[0] << " " << t.matrix[1] << " " + << t.matrix[2] << " " << t.matrix[3] << " ]"; + LOG(ERROR) << prefix << "[ " << t.matrix[4] << " " << t.matrix[5] << " " + << t.matrix[6] << " " << t.matrix[7] << " ]"; + LOG(ERROR) << prefix << "[ " << t.matrix[8] << " " << t.matrix[9] << " " + << t.matrix[10] << " " << t.matrix[11] << " ]"; + LOG(ERROR) << prefix << "[ " << t.matrix[12] << " " << t.matrix[13] << " " + << t.matrix[14] << " " << t.matrix[15] << " ]"; +} +#endif + Transform Transform::Identity() { return {}; } Transform Transform::Perspective(Float perspective) { auto transform = Transform{}; - transform.matrix[11] = -1.0 / perspective; + transform.matrix[11] = -1 / perspective; return transform; } @@ -86,6 +102,132 @@ Transform Transform::Rotate(Float x, Float y, Float z) { return transform; } +Transform::SRT Transform::ExtractSRT(Transform const &t) { + // First we need to extract translation, rotation, and scale from both + // matrices, in that order. Matrices must be in this form: [a b c d] [e f g h] + // [i j k l] + // [0 0 0 1] + // We also assume that all scale factors are non-negative, because in + assert( + t.matrix[12] == 0 && t.matrix[13] == 0 && t.matrix[14] == 0 && + t.matrix[15] == 1 && "Last row of matrix must be [0,0,0,1]"); + + // lhs: + // Translation: extract the values from the rightmost column + Float translationX = t.matrix[3]; + Float translationY = t.matrix[7]; + Float translationZ = t.matrix[11]; + + // Scale: the length of the first three column vectors + // TODO: do we need to do anything special for negative scale factors? + // the last element is a uniform scale factor + Float scaleX = t.matrix[15] * + sqrt(pow(t.matrix[0], 2) + pow(t.matrix[4], 2) + + pow(t.matrix[8], 2)); // sqrt(a^2 + e^2 + i^2) + Float scaleY = t.matrix[15] * + sqrt(pow(t.matrix[1], 2) + pow(t.matrix[5], 2) + + pow(t.matrix[9], 2)); // sqrt(b^2 + f^2 + j^2) + Float scaleZ = t.matrix[15] * + sqrt(pow(t.matrix[2], 2) + pow(t.matrix[6], 2) + + pow(t.matrix[10], 2)); // sqrt(c^2 + g^2 + k^2) + + Float rScaleFactorX = scaleX == 0 ? 1 : scaleX; + Float rScaleFactorY = scaleY == 0 ? 1 : scaleY; + Float rScaleFactorZ = scaleZ == 0 ? 1 : scaleZ; + + // Construct a rotation matrix and convert that to quaternions + auto rotationMatrix = std::array{t.matrix[0] / rScaleFactorX, + t.matrix[1] / rScaleFactorY, + t.matrix[2] / rScaleFactorZ, + 0, + t.matrix[4] / rScaleFactorX, + t.matrix[5] / rScaleFactorY, + t.matrix[6] / rScaleFactorZ, + 0, + t.matrix[8] / rScaleFactorX, + t.matrix[9] / rScaleFactorY, + t.matrix[10] / rScaleFactorZ, + 0, + 0, + 0, + 0, + 1}; + + Quaternion q = + Quaternion::fromRotationMatrix(rotationMatrix).normalize(); + + return Transform::SRT{ + translationX, translationY, translationZ, scaleX, scaleY, scaleZ, q}; +} + +Transform Transform::Interpolate( + float animationProgress, + Transform const &lhs, + Transform const &rhs) { + // Extract SRT for both sides + // This is extracted in the form: X,Y,Z coordinates for translations; X,Y,Z + // coordinates for scale; and a quaternion for rotation. + auto lhsSRT = ExtractSRT(lhs); + auto rhsSRT = ExtractSRT(rhs); + + // Interpolate translation and scale terms linearly (LERP) + Float translateX = + (lhsSRT.translationX + + (rhsSRT.translationX - lhsSRT.translationX) * animationProgress); + Float translateY = + (lhsSRT.translationY + + (rhsSRT.translationY - lhsSRT.translationY) * animationProgress); + Float translateZ = + (lhsSRT.translationZ + + (rhsSRT.translationZ - lhsSRT.translationZ) * animationProgress); + Float scaleX = + (lhsSRT.scaleX + (rhsSRT.scaleX - lhsSRT.scaleX) * animationProgress); + Float scaleY = + (lhsSRT.scaleY + (rhsSRT.scaleY - lhsSRT.scaleY) * animationProgress); + Float scaleZ = + (lhsSRT.scaleZ + (rhsSRT.scaleZ - lhsSRT.scaleZ) * animationProgress); + + // Use the quaternion vectors to produce an interpolated rotation via SLERP + // dot: cos of the angle between the two quaternion vectors + Quaternion q1 = lhsSRT.rotation; + Quaternion q2 = rhsSRT.rotation; + Float dot = q1.dot(q2); + // Clamp dot between -1 and 1 + dot = (dot < -1 ? -1 : (dot > 1 ? 1 : dot)); + // There are two ways of performing an identical slerp: q1 and -q1. + // If the dot-product is negative, we can multiply q1 by -1 and our animation + // will take the "short way" around instead of the "long way". + if (dot < 0) { + q1 = q1 * (Float)-1; + dot = dot * -1; + } + // Interpolated angle + Float theta = acosf(dot) * animationProgress; + + Transform rotation = Transform::Identity(); + + // Compute orthonormal basis + Quaternion orthonormalBasis = (q2 - q1 * dot); + + if (orthonormalBasis.abs() > 0) { + Quaternion orthonormalBasisNormalized = orthonormalBasis.normalize(); + + // Compute orthonormal basis + // Final quaternion result - slerp! + Quaternion resultingRotationVec = + (q1 * (Float)cos(theta) + + orthonormalBasisNormalized * (Float)sin(theta)) + .normalize(); + + // Convert quaternion to matrix + rotation.matrix = resultingRotationVec.toRotationMatrix4x4(); + } + + // Compose matrices and return + return (Scale(scaleX, scaleY, scaleZ) * rotation) * + Translate(translateX, translateY, translateZ); +} + bool Transform::operator==(Transform const &rhs) const { for (auto i = 0; i < 16; i++) { if (matrix[i] != rhs.matrix[i]) { diff --git a/ReactCommon/fabric/graphics/Transform.h b/ReactCommon/fabric/graphics/Transform.h index 49d8c095fd3fcd..357e2863d7df93 100644 --- a/ReactCommon/fabric/graphics/Transform.h +++ b/ReactCommon/fabric/graphics/Transform.h @@ -12,17 +12,41 @@ #include #include #include +#include + +#ifdef ANDROID +#include +#endif namespace facebook { namespace react { +struct ScaleRotationTranslation { + Float translationX; + Float translationY; + Float translationZ; + Float scaleX; + Float scaleY; + Float scaleZ; + Quaternion rotation; +}; + /* * Defines transform matrix to apply affine transformations. */ struct Transform { + using SRT = ScaleRotationTranslation; + std::array matrix{ {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; + /** + * For debugging only. Prints out the matrix. + */ +#ifdef RN_DEBUG_STRING_CONVERTIBLE + static void print(Transform const &t, std::string prefix); +#endif + /* * Returns the identity transform (`[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]`). */ @@ -56,6 +80,41 @@ struct Transform { static Transform RotateZ(Float angle); static Transform Rotate(Float angleX, Float angleY, Float angleZ); + /** + * Extract SRT (scale, rotation, transformation) from a Transform matrix. + * + * CAVEATS: + * 1. The input matrix must not have Skew applied. + * 2. Scaling factors must be non-negative. Scaling by a negative factor is + * equivalent to a rotation, and though it is possible to detect if 1 or + * 3 of the scale signs are flipped (but not two), it is not possible + * to detect WHICH of the scales are flipped. Thus, any animation + * that involves a negative scale factor will not crash but will + * interpolate over nonsensical values. + * 3. Another caveat is that if the animation interpolates TO a 90º + * rotation in the X, Y, or Z axis, the View will appear to suddenly + * explode in size. Interpolating THROUGH 90º is fine as long as you don't end + * up at 90º or close to it (89.99). The same is true for 0±90 and 360n+90, + * etc. + */ + static SRT ExtractSRT(Transform const &transform); + + /** + * Perform an interpolation between lhs and rhs, given progress. + * This first decomposes the matrices into translation, scale, and rotation, + * performs slerp between the two rotations, and a linear interpolation + * of scale and translation. + * + * @param progress + * @param lhs + * @param rhs + * @return + */ + static Transform Interpolate( + float animationProgress, + Transform const &lhs, + Transform const &rhs); + /* * Equality operators. */ @@ -72,6 +131,31 @@ struct Transform { * Concatenates (multiplies) transform matrices. */ Transform operator*(Transform const &rhs) const; + + /** + * Convert to folly::dynamic. + */ +#ifdef ANDROID + operator folly::dynamic() const { + return folly::dynamic::array( + matrix[0], + matrix[1], + matrix[2], + matrix[3], + matrix[4], + matrix[5], + matrix[6], + matrix[7], + matrix[8], + matrix[9], + matrix[10], + matrix[11], + matrix[12], + matrix[13], + matrix[14], + matrix[15]); + } +#endif }; /* diff --git a/ReactCommon/fabric/imagemanager/BUCK b/ReactCommon/fabric/imagemanager/BUCK index ba638f1d1e8098..0e1749e9973541 100644 --- a/ReactCommon/fabric/imagemanager/BUCK +++ b/ReactCommon/fabric/imagemanager/BUCK @@ -45,14 +45,12 @@ rn_xplat_cxx_library( ], prefix = "", ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( [ "platform/cxx/**/*.cpp", ], ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags() + [ "-DLOG_TAG=\"ReactNative\"", "-DWITH_FBSYSTRACE=1", @@ -88,6 +86,7 @@ rn_xplat_cxx_library( "platform/ios/**/*.mm", ], ), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/mapbuffer/BUCK b/ReactCommon/fabric/mapbuffer/BUCK index 89acfd5f302c30..fd968ba4362693 100644 --- a/ReactCommon/fabric/mapbuffer/BUCK +++ b/ReactCommon/fabric/mapbuffer/BUCK @@ -30,8 +30,8 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID), preprocessor_flags = [ diff --git a/ReactCommon/fabric/mounting/BUCK b/ReactCommon/fabric/mounting/BUCK index afa937d70bd0cc..46531cf3e49ffa 100644 --- a/ReactCommon/fabric/mounting/BUCK +++ b/ReactCommon/fabric/mounting/BUCK @@ -38,11 +38,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/mounting/Differentiator.cpp b/ReactCommon/fabric/mounting/Differentiator.cpp index 6661d5424407b2..e1ac64cbf35dca 100644 --- a/ReactCommon/fabric/mounting/Differentiator.cpp +++ b/ReactCommon/fabric/mounting/Differentiator.cpp @@ -65,7 +65,7 @@ class TinyMap final { inline Iterator end() { // `back()` asserts on the vector being non-empty - if (vector_.size() == 0 || numErased_ == vector_.size()) { + if (vector_.empty() || numErased_ == vector_.size()) { return nullptr; } @@ -112,7 +112,7 @@ class TinyMap final { */ inline Iterator begin_() { // `front()` asserts on the vector being non-empty - if (vector_.size() == 0 || vector_.size() == numErased_) { + if (vector_.empty() || vector_.size() == numErased_) { return nullptr; } @@ -125,9 +125,8 @@ class TinyMap final { * vector. */ inline void cleanVector(bool forceClean = false) { - if ((numErased_ < (vector_.size() / 2) && !forceClean) || - vector_.size() == 0 || numErased_ == 0 || - numErased_ == erasedAtFront_) { + if ((numErased_ < (vector_.size() / 2) && !forceClean) || vector_.empty() || + numErased_ == 0 || numErased_ == erasedAtFront_) { return; } @@ -190,7 +189,9 @@ static void sliceChildShadowNodeViewPairsRecursively( for (auto const &sharedChildShadowNode : shadowNode.getChildren()) { auto &childShadowNode = *sharedChildShadowNode; auto shadowView = ShadowView(childShadowNode); + auto origin = layoutOffset; if (shadowView.layoutMetrics != EmptyLayoutMetrics) { + origin += shadowView.layoutMetrics.frame.origin; shadowView.layoutMetrics.frame.origin += layoutOffset; } @@ -204,7 +205,7 @@ static void sliceChildShadowNodeViewPairsRecursively( } sliceChildShadowNodeViewPairsRecursively( - pairList, shadowView.layoutMetrics.frame.origin, childShadowNode); + pairList, origin, childShadowNode); } } } @@ -254,190 +255,12 @@ static_assert( std::is_move_assignable::value, "`ShadowViewNodePair::List` must be `move assignable`."); -static void calculateShadowViewMutationsClassic( +static void calculateShadowViewMutations( ShadowViewMutation::List &mutations, ShadowView const &parentShadowView, ShadowViewNodePair::List &&oldChildPairs, ShadowViewNodePair::List &&newChildPairs) { - // This version of the algorithm is optimized for simplicity, - // not for performance or optimal result. - - if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) { - return; - } - - // Sorting pairs based on `orderIndex` if needed. - reorderInPlaceIfNeeded(oldChildPairs); - reorderInPlaceIfNeeded(newChildPairs); - - auto index = int{0}; - - // Maps inserted node tags to pointers to them in `newChildPairs`. - auto insertedPairs = TinyMap{}; - - // 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) { - // Totally different nodes, updating is impossible. - break; - } - - 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); - calculateShadowViewMutationsClassic( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - oldChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } - - int lastIndexAfterFirstStage = index; - - // Stage 2: Collecting `Insert` mutations - for (; index < newChildPairs.size(); index++) { - auto const &newChildPair = newChildPairs[index]; - - insertMutations.push_back(ShadowViewMutation::InsertMutation( - parentShadowView, newChildPair.shadowView, index)); - - insertedPairs.insert({newChildPair.shadowView.tag, &newChildPair}); - } - - // Stage 3: Collecting `Delete` and `Remove` mutations - for (index = lastIndexAfterFirstStage; index < oldChildPairs.size(); - index++) { - auto const &oldChildPair = oldChildPairs[index]; - - // Even if the old view was (re)inserted, we have to generate `remove` - // mutation. - removeMutations.push_back(ShadowViewMutation::RemoveMutation( - parentShadowView, oldChildPair.shadowView, index)); - - auto const it = insertedPairs.find(oldChildPair.shadowView.tag); - - if (it == insertedPairs.end()) { - // The old view was *not* (re)inserted. - // We have to generate `delete` mutation and apply the algorithm - // recursively. - 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. - calculateShadowViewMutationsClassic( - destructiveDownwardMutations, - oldChildPair.shadowView, - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode), - {}); - } else { - // The old view *was* (re)inserted. - // We have to call the algorithm recursively if the inserted view - // is *not* the same as removed one. - auto const &newChildPair = *it->second; - - if (newChildPair != oldChildPair) { - auto oldGrandChildPairs = - sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); - auto newGrandChildPairs = - sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutationsClassic( - *(newGrandChildPairs.size() ? &downwardMutations - : &destructiveDownwardMutations), - newChildPair.shadowView, - std::move(oldGrandChildPairs), - std::move(newGrandChildPairs)); - } - - // In any case we have to remove the view from `insertedPairs` as - // indication that the view was actually removed (which means that - // the view existed before), hence we don't have to generate - // `create` mutation. - insertedPairs.erase(it); - } - } - - // Stage 4: Collecting `Create` mutations - for (index = lastIndexAfterFirstStage; index < newChildPairs.size(); - index++) { - auto const &newChildPair = newChildPairs[index]; - - if (insertedPairs.find(newChildPair.shadowView.tag) == - insertedPairs.end()) { - // The new view was (re)inserted, so there is no need to create it. - continue; - } - - createMutations.push_back( - ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - - calculateShadowViewMutationsClassic( - 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)); -} - -static void calculateShadowViewMutationsOptimizedMoves( - ShadowViewMutation::List &mutations, - ShadowView const &parentShadowView, - ShadowViewNodePair::List &&oldChildPairs, - ShadowViewNodePair::List &&newChildPairs) { - if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) { + if (oldChildPairs.empty() && newChildPairs.empty()) { return; } @@ -479,7 +302,7 @@ static void calculateShadowViewMutationsOptimizedMoves( sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); auto newGrandChildPairs = sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), oldChildPair.shadowView, @@ -502,7 +325,7 @@ static void calculateShadowViewMutationsOptimizedMoves( // We also have to call the algorithm recursively to clean up the entire // subtree starting from the removed view. - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( destructiveDownwardMutations, oldChildPair.shadowView, sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode), @@ -519,7 +342,7 @@ static void calculateShadowViewMutationsOptimizedMoves( createMutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( downwardMutations, newChildPair.shadowView, {}, @@ -576,7 +399,7 @@ static void calculateShadowViewMutationsOptimizedMoves( sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); auto newGrandChildPairs = sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), oldChildPair.shadowView, @@ -617,7 +440,7 @@ static void calculateShadowViewMutationsOptimizedMoves( sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode); auto newGrandChildPairs = sliceChildShadowNodeViewPairs(*newChildPair.shadowNode); - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations), oldChildPair.shadowView, @@ -641,7 +464,7 @@ static void calculateShadowViewMutationsOptimizedMoves( // We also have to call the algorithm recursively to clean up the // entire subtree starting from the removed view. - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( destructiveDownwardMutations, oldChildPair.shadowView, sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode), @@ -677,7 +500,7 @@ static void calculateShadowViewMutationsOptimizedMoves( createMutations.push_back( ShadowViewMutation::CreateMutation(newChildPair.shadowView)); - calculateShadowViewMutationsOptimizedMoves( + calculateShadowViewMutations( downwardMutations, newChildPair.shadowView, {}, @@ -717,7 +540,6 @@ static void calculateShadowViewMutationsOptimizedMoves( } ShadowViewMutation::List calculateShadowViewMutations( - DifferentiatorMode differentiatorMode, ShadowNode const &oldRootShadowNode, ShadowNode const &newRootShadowNode) { SystraceSection s("calculateShadowViewMutations"); @@ -736,19 +558,11 @@ ShadowViewMutation::List calculateShadowViewMutations( ShadowView(), oldRootShadowView, newRootShadowView, -1)); } - if (differentiatorMode == DifferentiatorMode::Classic) { - calculateShadowViewMutationsClassic( - mutations, - ShadowView(oldRootShadowNode), - sliceChildShadowNodeViewPairs(oldRootShadowNode), - sliceChildShadowNodeViewPairs(newRootShadowNode)); - } else { - calculateShadowViewMutationsOptimizedMoves( - mutations, - ShadowView(oldRootShadowNode), - sliceChildShadowNodeViewPairs(oldRootShadowNode), - sliceChildShadowNodeViewPairs(newRootShadowNode)); - } + calculateShadowViewMutations( + mutations, + ShadowView(oldRootShadowNode), + sliceChildShadowNodeViewPairs(oldRootShadowNode), + sliceChildShadowNodeViewPairs(newRootShadowNode)); return mutations; } diff --git a/ReactCommon/fabric/mounting/Differentiator.h b/ReactCommon/fabric/mounting/Differentiator.h index 96adb5b6ed536c..a7afd4f21c6b6f 100644 --- a/ReactCommon/fabric/mounting/Differentiator.h +++ b/ReactCommon/fabric/mounting/Differentiator.h @@ -21,7 +21,6 @@ enum class DifferentiatorMode { Classic, OptimizedMoves }; * The list of mutations might be and might not be optimal. */ ShadowViewMutationList calculateShadowViewMutations( - DifferentiatorMode differentiatorMode, ShadowNode const &oldRootShadowNode, ShadowNode const &newRootShadowNode); diff --git a/ReactCommon/fabric/mounting/MountingCoordinator.cpp b/ReactCommon/fabric/mounting/MountingCoordinator.cpp index 396740c1fd9d70..ad1552f1569102 100644 --- a/ReactCommon/fabric/mounting/MountingCoordinator.cpp +++ b/ReactCommon/fabric/mounting/MountingCoordinator.cpp @@ -19,9 +19,12 @@ namespace facebook { namespace react { -MountingCoordinator::MountingCoordinator(ShadowTreeRevision baseRevision) +MountingCoordinator::MountingCoordinator( + ShadowTreeRevision baseRevision, + MountingOverrideDelegate *delegate) : surfaceId_(baseRevision.getRootShadowNode().getSurfaceId()), - baseRevision_(baseRevision) { + baseRevision_(baseRevision), + mountingOverrideDelegate_(delegate) { #ifdef RN_SHADOW_TREE_INTROSPECTION stubViewTree_ = stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode()); #endif @@ -66,33 +69,30 @@ bool MountingCoordinator::waitForTransaction( lock, timeout, [this]() { return lastRevision_.has_value(); }); } -better::optional MountingCoordinator::pullTransaction( - DifferentiatorMode differentiatorMode) const { - std::lock_guard lock(mutex_); - - if (!lastRevision_.has_value()) { - return {}; - } - - number_++; +void MountingCoordinator::updateBaseRevision( + ShadowTreeRevision const &baseRevision) const { + baseRevision_ = std::move(baseRevision); +} - auto telemetry = lastRevision_->getTelemetry(); - telemetry.willDiff(); +void MountingCoordinator::resetLatestRevision() const { + lastRevision_.reset(); +} - auto mutations = calculateShadowViewMutations( - differentiatorMode, - baseRevision_.getRootShadowNode(), - lastRevision_->getRootShadowNode()); +#ifdef RN_SHADOW_TREE_INTROSPECTION +void MountingCoordinator::validateTransactionAgainstStubViewTree( + ShadowViewMutationList const &mutations, + bool assertEquality) const { + std::string line; - telemetry.didDiff(); + std::stringstream ssMutations(getDebugDescription(mutations, {})); + while (std::getline(ssMutations, line, '\n')) { + LOG(ERROR) << "Mutations:" << line; + } -#ifdef RN_SHADOW_TREE_INTROSPECTION stubViewTree_.mutate(mutations); auto stubViewTree = stubViewTreeFromShadowNode(lastRevision_->getRootShadowNode()); - std::string line; - std::stringstream ssOldTree( baseRevision_.getRootShadowNode().getDebugDescription()); while (std::getline(ssOldTree, line, '\n')) { @@ -105,19 +105,65 @@ better::optional MountingCoordinator::pullTransaction( LOG(ERROR) << "New tree:" << line; } - std::stringstream ssMutations(getDebugDescription(mutations, {})); - while (std::getline(ssMutations, line, '\n')) { - LOG(ERROR) << "Mutations:" << line; + if (assertEquality) { + assert(stubViewTree_ == stubViewTree); + } +} +#endif + +better::optional MountingCoordinator::pullTransaction() + const { + std::lock_guard lock(mutex_); + + bool shouldOverridePullTransaction = mountingOverrideDelegate_ != nullptr && + mountingOverrideDelegate_->shouldOverridePullTransaction(); + + if (!shouldOverridePullTransaction && !lastRevision_.has_value()) { + return {}; + } + + number_++; + + ShadowViewMutation::List diffMutations{}; + auto telemetry = + (lastRevision_.hasValue() ? lastRevision_->getTelemetry() + : MountingTelemetry{}); + if (lastRevision_.hasValue()) { + telemetry.willDiff(); + + diffMutations = calculateShadowViewMutations( + baseRevision_.getRootShadowNode(), lastRevision_->getRootShadowNode()); + + telemetry.didDiff(); } - assert(stubViewTree_ == stubViewTree); + better::optional transaction{}; + + // The override delegate can provide custom mounting instructions, + // even if there's no `lastRevision_`. Consider cases of animation frames + // in between React tree updates. + if (shouldOverridePullTransaction) { + transaction = mountingOverrideDelegate_->pullTransaction( + surfaceId_, number_, telemetry, std::move(diffMutations)); + } else if (lastRevision_.hasValue()) { + transaction = MountingTransaction{ + surfaceId_, number_, std::move(diffMutations), telemetry}; + } + + if (lastRevision_.hasValue()) { +#ifdef RN_SHADOW_TREE_INTROSPECTION + // Only validate non-animated transactions - it's garbage to validate + // animated transactions, since the stub view tree likely won't match + // the committed tree during an animation. + this->validateTransactionAgainstStubViewTree( + transaction->getMutations(), !shouldOverridePullTransaction); #endif - baseRevision_ = std::move(*lastRevision_); - lastRevision_.reset(); + baseRevision_ = std::move(*lastRevision_); + lastRevision_.reset(); + } - return MountingTransaction{ - surfaceId_, number_, std::move(mutations), telemetry}; + return transaction; } } // namespace react diff --git a/ReactCommon/fabric/mounting/MountingCoordinator.h b/ReactCommon/fabric/mounting/MountingCoordinator.h index 3926f49618b276..6976f35f48b401 100644 --- a/ReactCommon/fabric/mounting/MountingCoordinator.h +++ b/ReactCommon/fabric/mounting/MountingCoordinator.h @@ -11,8 +11,10 @@ #include #include +#include #include #include +#include "ShadowTreeRevision.h" #ifdef RN_SHADOW_TREE_INTROSPECTION #include @@ -33,10 +35,12 @@ class MountingCoordinator final { using Shared = std::shared_ptr; /* - * The constructor is ment to be used only inside `ShadowTree`, and it's + * 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); + MountingCoordinator( + ShadowTreeRevision baseRevision, + MountingOverrideDelegate *delegate); /* * Returns the id of the surface that the coordinator belongs to. @@ -52,8 +56,7 @@ class MountingCoordinator final { * However, a consumer should always call it on the same thread (e.g. on the * main thread) or ensure sequentiality of mount transactions separately. */ - better::optional pullTransaction( - DifferentiatorMode differentiatorMode) const; + better::optional pullTransaction() const; /* * Blocks the current thread until a new mounting transaction is available or @@ -66,12 +69,20 @@ class MountingCoordinator final { */ bool waitForTransaction(std::chrono::duration timeout) const; - private: - friend class ShadowTree; + /* + * Methods from this section are meant to be used by + * `MountingOverrideDelegate` only. + */ + public: + void updateBaseRevision(ShadowTreeRevision const &baseRevision) const; + void resetLatestRevision() const; /* * Methods from this section are meant to be used by `ShadowTree` only. */ + private: + friend class ShadowTree; + void push(ShadowTreeRevision &&revision) const; /* @@ -79,7 +90,7 @@ class MountingCoordinator final { * Generating a `MountingTransaction` requires some resources which the * `MountingCoordinator` does not own (e.g. `ComponentDescriptor`s). Revoking * committed revisions allows the owner (a Shadow Tree) to make sure that - * those resources will not be accessed (e.g. by the Mouting Layer). + * those resources will not be accessed (e.g. by the Mounting Layer). */ void revoke() const; @@ -91,8 +102,12 @@ class MountingCoordinator final { mutable better::optional lastRevision_{}; mutable MountingTransaction::Number number_{0}; mutable std::condition_variable signal_; + mutable MountingOverrideDelegate *mountingOverrideDelegate_{nullptr}; #ifdef RN_SHADOW_TREE_INTROSPECTION + void validateTransactionAgainstStubViewTree( + ShadowViewMutationList const &mutations, + bool assertEquality) const; mutable StubViewTree stubViewTree_; // Protected by `mutex_`. #endif }; diff --git a/ReactCommon/fabric/mounting/MountingOverrideDelegate.h b/ReactCommon/fabric/mounting/MountingOverrideDelegate.h new file mode 100644 index 00000000000000..7f642078101e38 --- /dev/null +++ b/ReactCommon/fabric/mounting/MountingOverrideDelegate.h @@ -0,0 +1,47 @@ +/* + * 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 + +#pragma once + +namespace facebook { +namespace react { + +class MountingCoordinator; + +/** + * Generic interface for anything that needs to override specific + * MountingCoordinator methods. This is for platform-specific escape hatches + * like animations. + */ +class MountingOverrideDelegate { + public: + virtual bool shouldOverridePullTransaction() const = 0; + virtual ~MountingOverrideDelegate(){}; + + /** + * Delegates that override this method are responsible for: + * + * - Returning a MountingTransaction with mutations + * - Calling + * - Telemetry, if appropriate + * + * @param surfaceId + * @param number + * @param mountingCoordinator + * @return + */ + virtual better::optional pullTransaction( + SurfaceId surfaceId, + MountingTransaction::Number number, + MountingTelemetry const &telemetry, + ShadowViewMutationList mutations) const = 0; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/mounting/MountingTransaction.h b/ReactCommon/fabric/mounting/MountingTransaction.h index a13526f8a900e8..0a0c68ab6d0ea9 100644 --- a/ReactCommon/fabric/mounting/MountingTransaction.h +++ b/ReactCommon/fabric/mounting/MountingTransaction.h @@ -18,7 +18,7 @@ namespace react { * particularly list of mutations and meta-data associated with the commit. * Movable and copyable, but moving is strongly encouraged. * Beware: A moved-from object of this type has unspecified value and accessing - * that is UB. + * that is UB (Undefined Behaviour). */ class MountingTransaction final { public: diff --git a/ReactCommon/fabric/mounting/ShadowTree.cpp b/ReactCommon/fabric/mounting/ShadowTree.cpp index 654d26fed0558a..d6a40be91705ef 100644 --- a/ReactCommon/fabric/mounting/ShadowTree.cpp +++ b/ReactCommon/fabric/mounting/ShadowTree.cpp @@ -41,7 +41,7 @@ static ShadowNode::Unshared progressState(ShadowNode const &shadowNode) { } auto newChildren = ShadowNode::ListOfShared{}; - if (shadowNode.getChildren().size() > 0) { + if (!shadowNode.getChildren().empty()) { auto index = size_t{0}; for (auto const &childNode : shadowNode.getChildren()) { auto newChildNode = progressState(*childNode); @@ -170,7 +170,7 @@ static void updateMountedFlag( return; } - if (oldChildren.size() == 0 && newChildren.size() == 0) { + if (oldChildren.empty() && newChildren.empty()) { // Both lists are empty, nothing to do. return; } @@ -221,7 +221,8 @@ ShadowTree::ShadowTree( LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext, RootComponentDescriptor const &rootComponentDescriptor, - ShadowTreeDelegate const &delegate) + ShadowTreeDelegate const &delegate, + MountingOverrideDelegate *mountingOverrideDelegate) : surfaceId_(surfaceId), delegate_(delegate) { const auto noopEventEmitter = std::make_shared( nullptr, -1, std::shared_ptr()); @@ -240,7 +241,7 @@ ShadowTree::ShadowTree( family)); mountingCoordinator_ = std::make_shared( - ShadowTreeRevision{rootShadowNode_, 0, {}}); + ShadowTreeRevision{rootShadowNode_, 0, {}}, mountingOverrideDelegate); } ShadowTree::~ShadowTree() { @@ -357,7 +358,7 @@ bool ShadowTree::tryCommit( mountingCoordinator_->push( ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry}); - delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_); + notifyDelegatesOfUpdates(); return true; } @@ -398,5 +399,9 @@ void ShadowTree::emitLayoutEvents( } } +void ShadowTree::notifyDelegatesOfUpdates() const { + delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/mounting/ShadowTree.h b/ReactCommon/fabric/mounting/ShadowTree.h index f0c904d5159a0c..a4606ba9a88dd5 100644 --- a/ReactCommon/fabric/mounting/ShadowTree.h +++ b/ReactCommon/fabric/mounting/ShadowTree.h @@ -18,6 +18,7 @@ #include #include #include +#include "MountingOverrideDelegate.h" namespace facebook { namespace react { @@ -38,7 +39,8 @@ class ShadowTree final { LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext, RootComponentDescriptor const &rootComponentDescriptor, - ShadowTreeDelegate const &delegate); + ShadowTreeDelegate const &delegate, + MountingOverrideDelegate *mountingOverrideDelegate); ~ShadowTree(); @@ -69,6 +71,13 @@ class ShadowTree final { */ void commitEmptyTree() const; + /** + * Forces the ShadowTree to ping its delegate that an update is available. + * Useful for animations on Android. + * @return + */ + void notifyDelegatesOfUpdates() const; + MountingCoordinator::Shared getMountingCoordinator() const; /* diff --git a/ReactCommon/fabric/mounting/ShadowTreeRegistry.cpp b/ReactCommon/fabric/mounting/ShadowTreeRegistry.cpp index f2792af365b24d..1564bbee14bd04 100644 --- a/ReactCommon/fabric/mounting/ShadowTreeRegistry.cpp +++ b/ReactCommon/fabric/mounting/ShadowTreeRegistry.cpp @@ -12,8 +12,7 @@ namespace react { ShadowTreeRegistry::~ShadowTreeRegistry() { assert( - registry_.size() == 0 && - "Deallocation of non-empty `ShadowTreeRegistry`."); + registry_.empty() && "Deallocation of non-empty `ShadowTreeRegistry`."); } void ShadowTreeRegistry::add(std::unique_ptr &&shadowTree) const { diff --git a/ReactCommon/fabric/mounting/ShadowTreeRevision.cpp b/ReactCommon/fabric/mounting/ShadowTreeRevision.cpp index 7e13c855403a7e..46f59d041804e7 100644 --- a/ReactCommon/fabric/mounting/ShadowTreeRevision.cpp +++ b/ReactCommon/fabric/mounting/ShadowTreeRevision.cpp @@ -22,6 +22,10 @@ MountingTelemetry const &ShadowTreeRevision::getTelemetry() const { return telemetry_; } +ShadowNode::Shared ShadowTreeRevision::getSharedRootShadowNode() { + return rootShadowNode_; +} + ShadowNode const &ShadowTreeRevision::getRootShadowNode() { return *rootShadowNode_; } diff --git a/ReactCommon/fabric/mounting/ShadowTreeRevision.h b/ReactCommon/fabric/mounting/ShadowTreeRevision.h index 71f68d0ed68dd2..ba89dc784f8364 100644 --- a/ReactCommon/fabric/mounting/ShadowTreeRevision.h +++ b/ReactCommon/fabric/mounting/ShadowTreeRevision.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -42,14 +43,21 @@ class ShadowTreeRevision final { */ MountingTelemetry const &getTelemetry() const; - private: - friend class MountingCoordinator; + /* + * Methods from this section are meant to be used by + * `MountingOverrideDelegate` only. + */ + public: + ShadowNode const &getRootShadowNode(); + ShadowNode::Shared getSharedRootShadowNode(); /* * Methods from this section are meant to be used by `MountingCoordinator` * only. */ - ShadowNode const &getRootShadowNode(); + private: + friend class MountingCoordinator; + Number getNumber() const; private: diff --git a/ReactCommon/fabric/mounting/ShadowView.cpp b/ReactCommon/fabric/mounting/ShadowView.cpp index 800308add54d1c..a5436df7dc35df 100644 --- a/ReactCommon/fabric/mounting/ShadowView.cpp +++ b/ReactCommon/fabric/mounting/ShadowView.cpp @@ -13,10 +13,10 @@ namespace facebook { namespace react { static LayoutMetrics layoutMetricsFromShadowNode(ShadowNode const &shadowNode) { - auto layotableShadowNode = + auto layoutableShadowNode = traitCast(&shadowNode); - return layotableShadowNode ? layotableShadowNode->getLayoutMetrics() - : EmptyLayoutMetrics; + return layoutableShadowNode ? layoutableShadowNode->getLayoutMetrics() + : EmptyLayoutMetrics; } ShadowView::ShadowView(const ShadowNode &shadowNode) diff --git a/ReactCommon/fabric/mounting/ShadowView.h b/ReactCommon/fabric/mounting/ShadowView.h index 1372b44928af37..fc51f9e9b1b6d5 100644 --- a/ReactCommon/fabric/mounting/ShadowView.h +++ b/ReactCommon/fabric/mounting/ShadowView.h @@ -66,7 +66,7 @@ struct ShadowViewNodePair final { ShadowNode const *shadowNode; /* - * The stored pointer to `ShadowNode` represents an indentity of the pair. + * The stored pointer to `ShadowNode` represents an identity of the pair. */ bool operator==(const ShadowViewNodePair &rhs) const; bool operator!=(const ShadowViewNodePair &rhs) const; diff --git a/ReactCommon/fabric/mounting/tests/MountingTest.cpp b/ReactCommon/fabric/mounting/tests/MountingTest.cpp index 52361e23dc5ab3..517347986614dc 100644 --- a/ReactCommon/fabric/mounting/tests/MountingTest.cpp +++ b/ReactCommon/fabric/mounting/tests/MountingTest.cpp @@ -227,8 +227,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) { }*/ // Calculating mutations. - auto mutations1 = calculateShadowViewMutations( - DifferentiatorMode::OptimizedMoves, *rootNodeV1, *rootNodeV2); + 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. @@ -244,8 +243,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) { assert(mutations1[1].index == 0); // Calculating mutations. - auto mutations2 = calculateShadowViewMutations( - DifferentiatorMode::OptimizedMoves, *rootNodeV2, *rootNodeV3); + 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. @@ -261,8 +259,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) { assert(mutations2[1].oldChildShadowView.tag == 100); // Calculating mutations. - auto mutations3 = calculateShadowViewMutations( - DifferentiatorMode::OptimizedMoves, *rootNodeV3, *rootNodeV4); + auto mutations3 = calculateShadowViewMutations(*rootNodeV3, *rootNodeV4); // The order and exact mutation instructions here may change at any time. // This test just ensures that any changes are intentional. @@ -283,8 +280,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) { assert(mutations3[3].index == 2); // Calculating mutations. - auto mutations4 = calculateShadowViewMutations( - DifferentiatorMode::OptimizedMoves, *rootNodeV4, *rootNodeV5); + 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. @@ -309,8 +305,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) { assert(mutations4[5].newChildShadowView.tag == 102); assert(mutations4[5].index == 3); - auto mutations5 = calculateShadowViewMutations( - DifferentiatorMode::OptimizedMoves, *rootNodeV5, *rootNodeV6); + 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. @@ -329,8 +324,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) { assert(mutations5[3].newChildShadowView.tag == 105); assert(mutations5[3].index == 3); - auto mutations6 = calculateShadowViewMutations( - DifferentiatorMode::OptimizedMoves, *rootNodeV6, *rootNodeV7); + 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. diff --git a/ReactCommon/fabric/mounting/tests/ShadowTreeLifeCycleTest.cpp b/ReactCommon/fabric/mounting/tests/ShadowTreeLifeCycleTest.cpp index ab2ad89b990449..c3aef70d4ab9c2 100644 --- a/ReactCommon/fabric/mounting/tests/ShadowTreeLifeCycleTest.cpp +++ b/ReactCommon/fabric/mounting/tests/ShadowTreeLifeCycleTest.cpp @@ -20,7 +20,6 @@ namespace facebook { namespace react { static void testShadowNodeTreeLifeCycle( - DifferentiatorMode differentiatorMode, uint_fast32_t seed, int treeSize, int repeats, @@ -72,8 +71,8 @@ static void testShadowNodeTreeLifeCycle( // Building an initial view hierarchy. auto viewTree = stubViewTreeFromShadowNode(*emptyRootNode); - viewTree.mutate(calculateShadowViewMutations( - differentiatorMode, *emptyRootNode, *currentRootNode)); + viewTree.mutate( + calculateShadowViewMutations(*emptyRootNode, *currentRootNode)); for (int j = 0; j < stages; j++) { auto nextRootNode = currentRootNode; @@ -99,8 +98,8 @@ static void testShadowNodeTreeLifeCycle( allNodes.push_back(nextRootNode); // Calculating mutations. - auto mutations = calculateShadowViewMutations( - differentiatorMode, *currentRootNode, *nextRootNode); + auto mutations = + calculateShadowViewMutations(*currentRootNode, *nextRootNode); // Mutating the view tree. viewTree.mutate(mutations); @@ -148,27 +147,8 @@ static void testShadowNodeTreeLifeCycle( using namespace facebook::react; -TEST(MountingTest, stableBiggerTreeFewerIterationsClassic) { - testShadowNodeTreeLifeCycle( - DifferentiatorMode::Classic, - /* seed */ 1, - /* size */ 512, - /* repeats */ 32, - /* stages */ 32); -} - -TEST(MountingTest, stableSmallerTreeMoreIterationsClassic) { - testShadowNodeTreeLifeCycle( - DifferentiatorMode::Classic, - /* seed */ 1, - /* size */ 16, - /* repeats */ 512, - /* stages */ 32); -} - TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMoves) { testShadowNodeTreeLifeCycle( - DifferentiatorMode::OptimizedMoves, /* seed */ 0, /* size */ 512, /* repeats */ 32, @@ -177,7 +157,6 @@ TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMoves) { TEST(MountingTest, stableSmallerTreeMoreIterationsOptimizedMoves) { testShadowNodeTreeLifeCycle( - DifferentiatorMode::OptimizedMoves, /* seed */ 0, /* size */ 16, /* repeats */ 512, diff --git a/ReactCommon/fabric/mounting/tests/StateReconciliationTest.cpp b/ReactCommon/fabric/mounting/tests/StateReconciliationTest.cpp index 19ba086ca2e3e3..665f18bcf6c0c4 100644 --- a/ReactCommon/fabric/mounting/tests/StateReconciliationTest.cpp +++ b/ReactCommon/fabric/mounting/tests/StateReconciliationTest.cpp @@ -103,7 +103,8 @@ TEST(StateReconciliationTest, testStateReconciliation) { LayoutConstraints{}, LayoutContext{}, rootComponentDescriptor, - shadowTreeDelegate}; + shadowTreeDelegate, + nullptr}; shadowTree.commit( [&](RootShadowNode::Shared const &oldRootShadowNode) { diff --git a/ReactCommon/fabric/scheduler/BUCK b/ReactCommon/fabric/scheduler/BUCK index e65958365b3007..fc4a94488bdfc1 100644 --- a/ReactCommon/fabric/scheduler/BUCK +++ b/ReactCommon/fabric/scheduler/BUCK @@ -36,11 +36,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/scheduler/Scheduler.cpp b/ReactCommon/fabric/scheduler/Scheduler.cpp index e9db21ae7e7812..d4dafd716b69c9 100644 --- a/ReactCommon/fabric/scheduler/Scheduler.cpp +++ b/ReactCommon/fabric/scheduler/Scheduler.cpp @@ -13,15 +13,23 @@ #include #include #include +#include +#include #include #include #include +#ifdef RN_SHADOW_TREE_INTROSPECTION +#include +#include +#endif + namespace facebook { namespace react { Scheduler::Scheduler( SchedulerToolbox schedulerToolbox, + UIManagerAnimationDelegate *animationDelegate, SchedulerDelegate *delegate) { runtimeExecutor_ = schedulerToolbox.runtimeExecutor; @@ -29,8 +37,13 @@ Scheduler::Scheduler( schedulerToolbox.contextContainer ->at>("ReactNativeConfig"); + // Creating a container for future `EventDispatcher` instance. + eventDispatcher_ = + std::make_shared>(); + auto uiManager = std::make_shared(); auto eventOwnerBox = std::make_shared(); + eventOwnerBox->owner = eventDispatcher_; auto eventPipe = [uiManager]( jsi::Runtime &runtime, @@ -47,20 +60,24 @@ Scheduler::Scheduler( uiManager->updateState(stateUpdate); }; - eventDispatcher_ = std::make_shared( + // Creating an `EventDispatcher` instance inside the already allocated + // container (inside the optional). + eventDispatcher_->emplace( eventPipe, statePipe, schedulerToolbox.synchronousEventBeatFactory, schedulerToolbox.asynchronousEventBeatFactory, eventOwnerBox); - eventOwnerBox->owner = eventDispatcher_; + // Casting to `std::shared_ptr`. + auto eventDispatcher = + EventDispatcher::Shared{eventDispatcher_, &eventDispatcher_->value()}; componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory( - eventDispatcher_, schedulerToolbox.contextContainer); + eventDispatcher, schedulerToolbox.contextContainer); rootComponentDescriptor_ = std::make_unique( - ComponentDescriptorParameters{eventDispatcher_, nullptr, nullptr}); + ComponentDescriptorParameters{eventDispatcher, nullptr, nullptr}); uiManager->setDelegate(this); uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_); @@ -81,12 +98,22 @@ Scheduler::Scheduler( delegate_ = delegate; uiManager_ = uiManager; + if (animationDelegate != nullptr) { + animationDelegate->setComponentDescriptorRegistry( + componentDescriptorRegistry_); + } + uiManager_->setAnimationDelegate(animationDelegate); + #ifdef ANDROID enableNewStateReconciliation_ = reactNativeConfig_->getBool( "react_fabric:enable_new_state_reconciliation_android"); + removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( + "react_fabric:remove_outstanding_surfaces_on_destruction_android"); #else enableNewStateReconciliation_ = reactNativeConfig_->getBool( "react_fabric:enable_new_state_reconciliation_ios"); + removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( + "react_fabric:remove_outstanding_surfaces_on_destruction_ios"); #endif } @@ -109,10 +136,10 @@ Scheduler::~Scheduler() { }); assert( - surfaceIds.size() == 0 && + surfaceIds.empty() && "Scheduler was destroyed with outstanding Surfaces."); - if (surfaceIds.size() == 0) { + if (surfaceIds.empty()) { return; } @@ -131,6 +158,12 @@ Scheduler::~Scheduler() { uiManager_->getShadowTreeRegistry().visit( surfaceId, [](ShadowTree const &shadowTree) { shadowTree.commitEmptyTree(); }); + + // Removing surfaces is gated because it acquires mutex waiting for commits + // in flight; in theory, it can deadlock. + if (removeOutstandingSurfacesOnDestruction_) { + uiManager_->getShadowTreeRegistry().remove(surfaceId); + } } } @@ -139,7 +172,8 @@ void Scheduler::startSurface( const std::string &moduleName, const folly::dynamic &initialProps, const LayoutConstraints &layoutConstraints, - const LayoutContext &layoutContext) const { + const LayoutContext &layoutContext, + MountingOverrideDelegate *mountingOverrideDelegate) const { SystraceSection s("Scheduler::startSurface"); auto shadowTree = std::make_unique( @@ -147,7 +181,8 @@ void Scheduler::startSurface( layoutConstraints, layoutContext, *rootComponentDescriptor_, - *uiManager_); + *uiManager_, + mountingOverrideDelegate); shadowTree->setEnableNewStateReconciliation(enableNewStateReconciliation_); @@ -168,7 +203,7 @@ void Scheduler::renderTemplateToSurface( const std::string &uiTemplate) { SystraceSection s("Scheduler::renderTemplateToSurface"); try { - if (uiTemplate.size() == 0) { + if (uiTemplate.empty()) { return; } NativeModuleRegistry nMR; @@ -291,6 +326,12 @@ SchedulerDelegate *Scheduler::getDelegate() const { return delegate_; } +#pragma mark - UIManagerAnimationDelegate + +void Scheduler::animationTick() const { + uiManager_->animationTick(); +} + #pragma mark - UIManagerDelegate void Scheduler::uiManagerDidFinishTransaction( @@ -301,7 +342,6 @@ void Scheduler::uiManagerDidFinishTransaction( delegate_->schedulerDidFinishTransaction(mountingCoordinator); } } - void Scheduler::uiManagerDidCreateShadowNode( const ShadowNode::Shared &shadowNode) { SystraceSection s("Scheduler::uiManagerDidCreateShadowNode"); diff --git a/ReactCommon/fabric/scheduler/Scheduler.h b/ReactCommon/fabric/scheduler/Scheduler.h index 07799c62f1fa46..9ee114ec21edc1 100644 --- a/ReactCommon/fabric/scheduler/Scheduler.h +++ b/ReactCommon/fabric/scheduler/Scheduler.h @@ -12,13 +12,14 @@ #include #include -#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -31,7 +32,10 @@ namespace react { */ class Scheduler final : public UIManagerDelegate { public: - Scheduler(SchedulerToolbox schedulerToolbox, SchedulerDelegate *delegate); + Scheduler( + SchedulerToolbox schedulerToolbox, + UIManagerAnimationDelegate *animationDelegate, + SchedulerDelegate *delegate); ~Scheduler(); #pragma mark - Surface Management @@ -41,7 +45,8 @@ class Scheduler final : public UIManagerDelegate { const std::string &moduleName, const folly::dynamic &initialProps, const LayoutConstraints &layoutConstraints = {}, - const LayoutContext &layoutContext = {}) const; + const LayoutContext &layoutContext = {}, + MountingOverrideDelegate *mountingOverrideDelegate = nullptr) const; void renderTemplateToSurface( SurfaceId surfaceId, @@ -88,6 +93,13 @@ class Scheduler final : public UIManagerDelegate { void setDelegate(SchedulerDelegate *delegate); SchedulerDelegate *getDelegate() const; +#pragma mark - UIManagerAnimationDelegate + // This is not needed on iOS or any platform that has a "pull" instead of + // "push" MountingCoordinator model. This just tells the delegate an update + // is available and that it should `pullTransaction`; we may want to rename + // this to be more generic and not animation-specific. + void animationTick() const; + #pragma mark - UIManagerDelegate void uiManagerDidFinishTransaction( @@ -111,8 +123,21 @@ class Scheduler final : public UIManagerDelegate { RuntimeExecutor runtimeExecutor_; std::shared_ptr uiManager_; std::shared_ptr reactNativeConfig_; - EventDispatcher::Shared eventDispatcher_; + + /* + * 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 + * pointer to an optional: we construct the pointer first, share that with + * parts that need to have ownership (and only ownership) of that, and then + * fill the optional. + */ + std::shared_ptr> eventDispatcher_; + + /* + * Temporary flags. + */ bool enableNewStateReconciliation_{false}; + bool removeOutstandingSurfacesOnDestruction_{false}; }; } // namespace react diff --git a/ReactCommon/fabric/scheduler/SchedulerToolbox.h b/ReactCommon/fabric/scheduler/SchedulerToolbox.h index 87462d89377ff1..0f56dbcccb410b 100644 --- a/ReactCommon/fabric/scheduler/SchedulerToolbox.h +++ b/ReactCommon/fabric/scheduler/SchedulerToolbox.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -36,6 +37,11 @@ struct SchedulerToolbox final { */ RuntimeExecutor runtimeExecutor; + /* + * Represent connections with a platform-specific UI run loops. + */ + RunLoopObserver::Factory mainRunLoopObserverFactory; + /* * Asynchronous & synchronous event beats. * Represent connections with the platform-specific run loops and general diff --git a/ReactCommon/fabric/templateprocessor/BUCK b/ReactCommon/fabric/templateprocessor/BUCK index 168a55862964e0..ea5518b3818a5b 100644 --- a/ReactCommon/fabric/templateprocessor/BUCK +++ b/ReactCommon/fabric/templateprocessor/BUCK @@ -37,11 +37,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/textlayoutmanager/BUCK b/ReactCommon/fabric/textlayoutmanager/BUCK index 720f3d3e8e2344..90db8d8d71ecae 100644 --- a/ReactCommon/fabric/textlayoutmanager/BUCK +++ b/ReactCommon/fabric/textlayoutmanager/BUCK @@ -75,14 +75,12 @@ rn_xplat_cxx_library( ], prefix = "", ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( [ "platform/android/**/*.cpp", ], ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), force_static = True, ios_deps = [ @@ -110,6 +108,7 @@ rn_xplat_cxx_library( "platform/ios/**/*.mm", ], ), + labels = ["supermodule:xplat/default/public.react_native.infra"], macosx_tests_override = [], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ diff --git a/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp b/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp index 5a1aab941997e1..52fda0f7cd841f 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp +++ b/ReactCommon/fabric/textlayoutmanager/platform/android/TextLayoutManager.cpp @@ -50,7 +50,7 @@ TextMeasurement TextLayoutManager::doMeasure( } } auto env = Environment::current(); - auto attachmentPositions = env->NewIntArray(attachmentsCount * 2); + auto attachmentPositions = env->NewFloatArray(attachmentsCount * 2); static auto measure = jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") @@ -64,7 +64,7 @@ TextMeasurement TextLayoutManager::doMeasure( jfloat, jfloat, jfloat, - jintArray)>("measure"); + jfloatArray)>("measure"); auto minimumSize = layoutConstraints.minimumSize; auto maximumSize = layoutConstraints.maximumSize; @@ -93,7 +93,7 @@ TextMeasurement TextLayoutManager::doMeasure( maximumSize.height, attachmentPositions)); - jint *attachmentData = env->GetIntArrayElements(attachmentPositions, 0); + jfloat *attachmentData = env->GetFloatArrayElements(attachmentPositions, 0); auto attachments = TextMeasurement::Attachments{}; if (attachmentsCount > 0) { @@ -104,8 +104,8 @@ TextMeasurement TextLayoutManager::doMeasure( if (fragment["isAttachment"] == true) { float top = attachmentData[attachmentIndex * 2]; float left = attachmentData[attachmentIndex * 2 + 1]; - float width = fragment["width"].getInt(); - float height = fragment["height"].getInt(); + float width = (float)fragment["width"].getDouble(); + float height = (float)fragment["height"].getDouble(); auto rect = facebook::react::Rect{{left, top}, facebook::react::Size{width, height}}; diff --git a/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.cpp b/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.cpp index d48c788fdd0ccd..ffdfeae66d547e 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.cpp +++ b/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.cpp @@ -16,11 +16,11 @@ void *TextLayoutManager::getNativeTextLayoutManager() const { return self_; } -Size TextLayoutManager::measure( +TextMeasurement TextLayoutManager::measure( AttributedStringBox attributedStringBox, ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const { - return Size{0, 0}; + return TextMeasurement{{0, 0}, {}}; } } // namespace react diff --git a/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.h b/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.h index c4cd7a8318f45d..57d301995b3918 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.h +++ b/ReactCommon/fabric/textlayoutmanager/platform/cxx/TextLayoutManager.h @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace facebook { @@ -34,7 +35,7 @@ class TextLayoutManager { /* * Measures `attributedStringBox` using native text rendering infrastructure. */ - Size measure( + TextMeasurement measure( AttributedStringBox attributedStringBox, ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const; diff --git a/ReactCommon/fabric/uimanager/BUCK b/ReactCommon/fabric/uimanager/BUCK index 0d46cf3f3fea90..aa31fcae2af8c2 100644 --- a/ReactCommon/fabric/uimanager/BUCK +++ b/ReactCommon/fabric/uimanager/BUCK @@ -37,11 +37,10 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h b/ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.h new file mode 100644 index 00000000000000..b87b96f68f73cc --- /dev/null +++ b/ReactCommon/fabric/uimanager/LayoutAnimationStatusDelegate.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 + +namespace facebook { +namespace react { + +class LayoutAnimationStatusDelegate { + public: + /** + * Called when the LayoutAnimation engine state changes from animation nothing + * to animating something. This will only be called when you go from 0 to N>0 + * active animations, N to N+1 animations will not result in this being + * called. + */ + virtual void onAnimationStarted() = 0; + + /** + * Called when the LayoutAnimation engine completes all pending animations. + */ + virtual void onAllAnimationsComplete() = 0; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/uimanager/UIManager.cpp b/ReactCommon/fabric/uimanager/UIManager.cpp index e87280c58cd28a..b4910cee67282c 100644 --- a/ReactCommon/fabric/uimanager/UIManager.cpp +++ b/ReactCommon/fabric/uimanager/UIManager.cpp @@ -124,31 +124,39 @@ void UIManager::clearJSResponder() const { } } -ShadowNode::Shared const *UIManager::getNewestCloneOfShadowNode( +ShadowNode::Shared UIManager::getNewestCloneOfShadowNode( ShadowNode const &shadowNode) const { auto findNewestChildInParent = - [&](auto const &parentNode) -> ShadowNode::Shared const * { + [&](auto const &parentNode) -> ShadowNode::Shared { for (auto const &child : parentNode.getChildren()) { if (ShadowNode::sameFamily(*child, shadowNode)) { - return &child; + return child; } } return nullptr; }; - ShadowNode const *ancestorShadowNode; + auto ancestorShadowNode = ShadowNode::Shared{}; shadowTreeRegistry_.visit( shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) { shadowTree.tryCommit( [&](RootShadowNode::Shared const &oldRootShadowNode) { - ancestorShadowNode = oldRootShadowNode.get(); + ancestorShadowNode = oldRootShadowNode; return nullptr; }, true); }); + if (!ancestorShadowNode) { + return nullptr; + } + auto ancestors = shadowNode.getFamily().getAncestors(*ancestorShadowNode); + if (ancestors.empty()) { + return nullptr; + } + return findNewestChildInParent(ancestors.rbegin()->first.get()); } @@ -156,7 +164,7 @@ ShadowNode::Shared UIManager::findNodeAtPoint( ShadowNode::Shared const &node, Point point) const { return LayoutableShadowNode::findNodeAtPoint( - *getNewestCloneOfShadowNode(*node), point); + getNewestCloneOfShadowNode(*node), point); } void UIManager::setNativeProps( @@ -190,6 +198,10 @@ LayoutMetrics UIManager::getRelativeLayoutMetrics( LayoutableShadowNode::LayoutInspectingPolicy policy) const { SystraceSection s("UIManager::getRelativeLayoutMetrics"); + // We might store here an owning pointer to `ancestorShadowNode` to ensure + // that the node is not deallocated during method execution lifetime. + auto owningAncestorShadowNode = ShadowNode::Shared{}; + if (!ancestorShadowNode) { shadowTreeRegistry_.visit( shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) { @@ -201,26 +213,22 @@ LayoutMetrics UIManager::getRelativeLayoutMetrics( true); }); } else { - ancestorShadowNode = getNewestCloneOfShadowNode(*ancestorShadowNode)->get(); + // It is possible for JavaScript (or other callers) to have a reference + // to a previous version of ShadowNodes, but we enforce that + // metrics are only calculated on most recently committed versions. + owningAncestorShadowNode = getNewestCloneOfShadowNode(*ancestorShadowNode); + ancestorShadowNode = owningAncestorShadowNode.get(); } - // Get latest version of both the ShadowNode and its ancestor. - // It is possible for JS (or other callers) to have a reference - // to a previous version of ShadowNodes, but we enforce that - // metrics are only calculated on most recently committed versions. - auto newestShadowNode = getNewestCloneOfShadowNode(shadowNode); - - auto layoutableShadowNode = - traitCast(newestShadowNode->get()); auto layoutableAncestorShadowNode = traitCast(ancestorShadowNode); - if (!layoutableShadowNode || !layoutableAncestorShadowNode) { + if (!layoutableAncestorShadowNode) { return EmptyLayoutMetrics; } - return layoutableShadowNode->getRelativeLayoutMetrics( - *layoutableAncestorShadowNode, policy); + return LayoutableShadowNode::computeRelativeLayoutMetrics( + shadowNode.getFamily(), *layoutableAncestorShadowNode, policy); } void UIManager::updateState(StateUpdate const &stateUpdate) const { @@ -260,9 +268,15 @@ void UIManager::dispatchCommand( } void UIManager::configureNextLayoutAnimation( - const folly::dynamic config, + RawValue const &config, SharedEventTarget successCallback, - SharedEventTarget errorCallback) const {} + SharedEventTarget errorCallback) const { + if (animationDelegate_) { + animationDelegate_->uiManagerDidConfigureNextLayoutAnimation( + config, successCallback, errorCallback); + } +} + void UIManager::setComponentDescriptorRegistry( const SharedComponentDescriptorRegistry &componentDescriptorRegistry) { componentDescriptorRegistry_ = componentDescriptorRegistry; @@ -302,5 +316,22 @@ void UIManager::shadowTreeDidFinishTransaction( } } +#pragma mark - UIManagerAnimationDelegate + +void UIManager::setAnimationDelegate( + UIManagerAnimationDelegate *delegate) const { + animationDelegate_ = delegate; +} + +void UIManager::animationTick() { + if (animationDelegate_ != nullptr && + animationDelegate_->shouldAnimateFrame()) { + shadowTreeRegistry_.enumerate( + [&](ShadowTree const &shadowTree, bool &stop) { + shadowTree.notifyDelegatesOfUpdates(); + }); + } +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/uimanager/UIManager.h b/ReactCommon/fabric/uimanager/UIManager.h index 956f459f935cee..543bf97cfa1852 100644 --- a/ReactCommon/fabric/uimanager/UIManager.h +++ b/ReactCommon/fabric/uimanager/UIManager.h @@ -12,11 +12,13 @@ #include #include +#include #include #include #include #include #include +#include #include namespace facebook { @@ -39,6 +41,15 @@ class UIManager final : public ShadowTreeDelegate { void setDelegate(UIManagerDelegate *delegate); UIManagerDelegate *getDelegate(); + /** + * Sets and gets the UIManager's Animation APIs delegate. + * The delegate is stored as a raw pointer, so the owner must null + * the pointer before being destroyed. + */ + void setAnimationDelegate(UIManagerAnimationDelegate *delegate) const; + + void animationTick(); + /* * Provides access to a UIManagerBindging. * The `callback` methods will not be called if the internal pointer to @@ -92,7 +103,7 @@ class UIManager final : public ShadowTreeDelegate { ShadowNode::Shared const &shadowNode, Point point) const; - ShadowNode::Shared const *getNewestCloneOfShadowNode( + ShadowNode::Shared getNewestCloneOfShadowNode( ShadowNode const &shadowNode) const; /* @@ -121,13 +132,15 @@ class UIManager final : public ShadowTreeDelegate { * This API configures a global LayoutAnimation starting from the root node. */ void configureNextLayoutAnimation( - const folly::dynamic config, + RawValue const &config, SharedEventTarget successCallback, SharedEventTarget errorCallback) const; + ShadowTreeRegistry const &getShadowTreeRegistry() const; SharedComponentDescriptorRegistry componentDescriptorRegistry_; UIManagerDelegate *delegate_; + mutable UIManagerAnimationDelegate *animationDelegate_{nullptr}; UIManagerBinding *uiManagerBinding_; ShadowTreeRegistry shadowTreeRegistry_{}; }; diff --git a/ReactCommon/fabric/uimanager/UIManagerAnimationDelegate.h b/ReactCommon/fabric/uimanager/UIManagerAnimationDelegate.h new file mode 100644 index 00000000000000..9dcbef33e1228c --- /dev/null +++ b/ReactCommon/fabric/uimanager/UIManagerAnimationDelegate.h @@ -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. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +class UIManagerAnimationDelegate { + public: + virtual ~UIManagerAnimationDelegate(){}; + + /* + * Configure a LayoutAnimation. + * TODO: need SurfaceId here + */ + virtual void uiManagerDidConfigureNextLayoutAnimation( + RawValue const &config, + SharedEventTarget successCallback, + SharedEventTarget errorCallback) const = 0; + + /** + * Set ComponentDescriptor registry. + * + * @param componentDescriptorRegistry + */ + virtual void setComponentDescriptorRegistry( + const SharedComponentDescriptorRegistry &componentDescriptorRegistry) = 0; + + /** + * Only needed on Android to drive animations. + */ + virtual bool shouldAnimateFrame() const = 0; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/uimanager/UIManagerBinding.cpp b/ReactCommon/fabric/uimanager/UIManagerBinding.cpp index 4c50095e4416a8..92d20a915cd600 100644 --- a/ReactCommon/fabric/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/fabric/uimanager/UIManagerBinding.cpp @@ -619,16 +619,14 @@ jsi::Value UIManagerBinding::get( const jsi::Value *arguments, size_t count) -> jsi::Value { uiManager->configureNextLayoutAnimation( - commandArgsFromValue( - runtime, - arguments[0]), // TODO T66507273: do a better job of parsing - // these arguments into a real struct / use a - // C++ typed object instead of folly::dynamic + // TODO: pass in JSI value instead of folly::dynamic to RawValue + RawValue(commandArgsFromValue(runtime, arguments[0])), eventTargetFromValue(runtime, arguments[1], -1), eventTargetFromValue(runtime, arguments[2], -1)); return jsi::Value::undefined(); }); } + return jsi::Value::undefined(); } diff --git a/ReactCommon/fabric/uimanager/UIManagerBinding.h b/ReactCommon/fabric/uimanager/UIManagerBinding.h index 75485e6c8ce695..0ecf00c9dd9d62 100644 --- a/ReactCommon/fabric/uimanager/UIManagerBinding.h +++ b/ReactCommon/fabric/uimanager/UIManagerBinding.h @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/ReactCommon/hermes/inspector/BUCK b/ReactCommon/hermes/inspector/BUCK index 9757b023533637..f3ea76a314efc8 100644 --- a/ReactCommon/hermes/inspector/BUCK +++ b/ReactCommon/hermes/inspector/BUCK @@ -50,9 +50,8 @@ fb_xplat_cxx_library( header_namespace = "hermes/inspector", exported_headers = CHROME_EXPORTED_HEADERS, compiler_flags = CFLAGS_BY_MODE[hermes_build_mode()], - fbandroid_labels = ["supermodule:android/default/public.hermes"], fbobjc_header_path_prefix = "hermes/inspector/chrome", - fbobjc_labels = ["supermodule:ios/default/public.hermes"], + labels = ["supermodule:xplat/default/public.hermes"], macosx_tests_override = [], tests = [":chrome-tests"], visibility = [ @@ -118,9 +117,8 @@ fb_xplat_cxx_library( fbandroid_deps = [ "//fbandroid/native/fb:fb", ], - fbandroid_labels = ["supermodule:android/default/public.hermes"], fbobjc_header_path_prefix = "hermes/inspector/detail", - fbobjc_labels = ["supermodule:ios/default/public.hermes"], + labels = ["supermodule:xplat/default/public.hermes"], macosx_tests_override = [], tests = [":detail-tests"], visibility = [ @@ -192,9 +190,8 @@ fb_xplat_cxx_library( exported_headers = INSPECTOR_EXPORTED_HEADERS, compiler_flags = CFLAGS_BY_MODE[hermes_build_mode()], cxx_tests = [":inspector-tests"], - fbandroid_labels = ["supermodule:android/default/public.hermes"], fbobjc_header_path_prefix = "hermes/inspector", - fbobjc_labels = ["supermodule:ios/default/public.hermes"], + labels = ["supermodule:xplat/default/public.hermes"], macosx_tests_override = [], visibility = [ "PUBLIC", diff --git a/ReactCommon/hermes/inspector/Inspector.cpp b/ReactCommon/hermes/inspector/Inspector.cpp index 3cca8ce1553b22..c2b58712119d55 100644 --- a/ReactCommon/hermes/inspector/Inspector.cpp +++ b/ReactCommon/hermes/inspector/Inspector.cpp @@ -120,6 +120,7 @@ Inspector::Inspector( std::lock_guard lock(mutex_); if (pauseOnFirstStatement) { + awaitingDebuggerOnStart_ = true; TRANSITION(std::make_unique(*this)); } else { TRANSITION(std::make_unique(*this)); @@ -137,6 +138,7 @@ Inspector::~Inspector() { void Inspector::installConsoleFunction( jsi::Object &console, + std::shared_ptr &originalConsole, const std::string &name, const std::string &chromeTypeDefault = "") { jsi::Runtime &rt = adapter_->getRuntime(); @@ -150,11 +152,22 @@ void Inspector::installConsoleFunction( rt, nameID, 1, - [weakInspector, chromeType]( + [weakInspector, originalConsole, name, chromeType]( jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *args, size_t count) { + if (originalConsole) { + auto val = originalConsole->getProperty(runtime, name.c_str()); + if (val.isObject()) { + auto obj = val.getObject(runtime); + if (obj.isFunction(runtime)) { + auto func = obj.getFunction(runtime); + func.call(runtime, args, count); + } + } + } + if (auto inspector = weakInspector.lock()) { jsi::Array argsArray(runtime, count); for (size_t index = 0; index < count; ++index) @@ -170,22 +183,28 @@ void Inspector::installConsoleFunction( void Inspector::installLogHandler() { jsi::Runtime &rt = adapter_->getRuntime(); auto console = jsi::Object(rt); - installConsoleFunction(console, "assert"); - installConsoleFunction(console, "clear"); - installConsoleFunction(console, "debug"); - installConsoleFunction(console, "dir"); - installConsoleFunction(console, "dirxml"); - installConsoleFunction(console, "error"); - installConsoleFunction(console, "group", "startGroup"); - installConsoleFunction(console, "groupCollapsed", "startGroupCollapsed"); - installConsoleFunction(console, "groupEnd", "endGroup"); - installConsoleFunction(console, "info"); - installConsoleFunction(console, "log"); - installConsoleFunction(console, "profile"); - installConsoleFunction(console, "profileEnd"); - installConsoleFunction(console, "table"); - installConsoleFunction(console, "trace"); - installConsoleFunction(console, "warn", "warning"); + auto val = rt.global().getProperty(rt, "console"); + std::shared_ptr originalConsole; + if (val.isObject()) { + originalConsole = std::make_shared(val.getObject(rt)); + } + installConsoleFunction(console, originalConsole, "assert"); + installConsoleFunction(console, originalConsole, "clear"); + installConsoleFunction(console, originalConsole, "debug"); + installConsoleFunction(console, originalConsole, "dir"); + installConsoleFunction(console, originalConsole, "dirxml"); + installConsoleFunction(console, originalConsole, "error"); + installConsoleFunction(console, originalConsole, "group", "startGroup"); + installConsoleFunction( + console, originalConsole, "groupCollapsed", "startGroupCollapsed"); + installConsoleFunction(console, originalConsole, "groupEnd", "endGroup"); + installConsoleFunction(console, originalConsole, "info"); + installConsoleFunction(console, originalConsole, "log"); + installConsoleFunction(console, originalConsole, "profile"); + installConsoleFunction(console, originalConsole, "profileEnd"); + installConsoleFunction(console, originalConsole, "table"); + installConsoleFunction(console, originalConsole, "trace"); + installConsoleFunction(console, originalConsole, "warn", "warning"); rt.global().setProperty(rt, "console", console); } @@ -661,6 +680,10 @@ bool Inspector::isExecutingSupersededFile() { return false; } +bool Inspector::isAwaitingDebuggerOnStart() { + return awaitingDebuggerOnStart_; +} + } // namespace inspector } // namespace hermes } // namespace facebook diff --git a/ReactCommon/hermes/inspector/Inspector.h b/ReactCommon/hermes/inspector/Inspector.h index bf4fc553c1da1f..ac00e670e74095 100644 --- a/ReactCommon/hermes/inspector/Inspector.h +++ b/ReactCommon/hermes/inspector/Inspector.h @@ -229,6 +229,16 @@ class Inspector : public facebook::hermes::debugger::EventObserver, facebook::hermes::debugger::Debugger &debugger, facebook::hermes::debugger::BreakpointID breakpointId) override; + /** + * Get whether we started with pauseOnFirstStatement, and have not yet had a + * debugger attach and ask to resume from that point. This matches the + * semantics of when CDP Debugger.runIfWaitingForDebugger should resume. + * + * It's not named "isPausedOnStart" because the VM and inspector is not + * necessarily paused; we could be in a RunningWaitPause state. + */ + bool isAwaitingDebuggerOnStart(); + private: friend class InspectorState; @@ -292,6 +302,7 @@ class Inspector : public facebook::hermes::debugger::EventObserver, void installConsoleFunction( jsi::Object &console, + std::shared_ptr &originalConsole, const std::string &name, const std::string &chromeType); @@ -333,6 +344,10 @@ class Inspector : public facebook::hermes::debugger::EventObserver, // Trigger a fake console.log if we're currently in a superseded file. void alertIfPausedInSupersededFile(); + + // Are we currently waiting for a debugger to attach, because we + // requested 'pauseOnFirstStatement'? + bool awaitingDebuggerOnStart_; }; } // namespace inspector diff --git a/ReactCommon/hermes/inspector/InspectorState.cpp b/ReactCommon/hermes/inspector/InspectorState.cpp index 7d6c35e05bbaee..38042872472c83 100644 --- a/ReactCommon/hermes/inspector/InspectorState.cpp +++ b/ReactCommon/hermes/inspector/InspectorState.cpp @@ -51,6 +51,10 @@ std::pair InspectorState::RunningDetached::didPause( nullptr, makeContinueCommand()); } +void InspectorState::RunningDetached::onEnter(InspectorState *previous) { + inspector_.awaitingDebuggerOnStart_ = false; +} + std::pair InspectorState::RunningDetached::enable() { return std::make_pair( InspectorState::Running::make(inspector_), true); @@ -172,6 +176,8 @@ void InspectorState::Running::onEnter(InspectorState *prevState) { inspector_.notifyScriptsLoaded(); } } + + inspector_.awaitingDebuggerOnStart_ = false; } void InspectorState::Running::detach( diff --git a/ReactCommon/hermes/inspector/InspectorState.h b/ReactCommon/hermes/inspector/InspectorState.h index 99cbff7e372c50..827f3149f43bb4 100644 --- a/ReactCommon/hermes/inspector/InspectorState.h +++ b/ReactCommon/hermes/inspector/InspectorState.h @@ -52,7 +52,7 @@ class InspectorState { */ /** - * detach clears all debuger state and transitions to RunningDetached. + * detach clears all debugger state and transitions to RunningDetached. */ virtual void detach(std::shared_ptr> promise) { // As we're not attached we'd like for the operation to be idempotent @@ -186,6 +186,8 @@ class InspectorState::RunningDetached : public InspectorState { std::pair didPause(MonitorLock &lock) override; std::pair enable() override; + void onEnter(InspectorState *prevState) override; + bool isRunningDetached() const override { return true; } diff --git a/ReactCommon/hermes/inspector/chrome/Connection.cpp b/ReactCommon/hermes/inspector/chrome/Connection.cpp index b7656bcf22c64d..6beac456b64fcf 100644 --- a/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -94,6 +94,7 @@ class Connection::Impl : public inspector::InspectorObserver, const m::heapProfiler::StopTrackingHeapObjectsRequest &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; private: std::vector makePropsFromScope( @@ -312,6 +313,9 @@ void Connection::Impl::onPause( note.reason = "exception"; break; case debugger::PauseReason::ScriptLoaded: { + // This case covers both wait-for-debugger and instrumentation + // breakpoints, since both are implemented as pauses on script load. + note.reason = "other"; note.hitBreakpoints = std::vector(); @@ -325,7 +329,8 @@ void Connection::Impl::onPause( // in the extremely unlikely event that it did *and* did it exactly // between us 1. checking that we should stop, and 2. adding the stop // reason here, then just resume and skip sending a pause notification. - if (note.hitBreakpoints->empty()) { + if (!inspector_->isAwaitingDebuggerOnStart() && + note.hitBreakpoints->empty()) { sendNotification = false; inspector_->resume(); } @@ -528,7 +533,7 @@ void Connection::Impl::handle( const m::heapProfiler::StopTrackingHeapObjectsRequest &req) { sendSnapshot( req.id, - "HeapSnapshot.takeHeapSnapshot", + "HeapSnapshot.stopTrackingHeapObjects", req.reportProgress && *req.reportProgress, /* stopStackTraceCapture */ true); } @@ -866,6 +871,16 @@ void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) { .thenError(sendErrorToClient(req.id)); } +void Connection::Impl::handle( + const m::runtime::RunIfWaitingForDebuggerRequest &req) { + if (inspector_->isAwaitingDebuggerOnStart()) { + sendResponseToClientViaExecutor(inspector_->resume(), req.id); + } else { + // We weren't awaiting a debugger. Just send an 'ok'. + sendResponseToClientViaExecutor(req.id); + } +} + /* * Send-to-client methods */ diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index d40c05e14727d9..e136e1c8f194a9 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<<575de63c36edd2a9a0f191501fd4f662>> +// @generated SignedSource<> #include "MessageTypes.h" @@ -50,6 +50,8 @@ std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { makeUnique}, {"Runtime.evaluate", makeUnique}, {"Runtime.getProperties", makeUnique}, + {"Runtime.runIfWaitingForDebugger", + makeUnique}, }; dynamic obj = folly::parseJson(str); @@ -783,6 +785,28 @@ void runtime::GetPropertiesRequest::accept(RequestHandler &handler) const { handler.handle(*this); } +runtime::RunIfWaitingForDebuggerRequest::RunIfWaitingForDebuggerRequest() + : Request("Runtime.runIfWaitingForDebugger") {} + +runtime::RunIfWaitingForDebuggerRequest::RunIfWaitingForDebuggerRequest( + const dynamic &obj) + : Request("Runtime.runIfWaitingForDebugger") { + assign(id, obj, "id"); + assign(method, obj, "method"); +} + +dynamic runtime::RunIfWaitingForDebuggerRequest::toDynamic() const { + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + return obj; +} + +void runtime::RunIfWaitingForDebuggerRequest::accept( + RequestHandler &handler) const { + handler.handle(*this); +} + /// Responses ErrorResponse::ErrorResponse(const dynamic &obj) { assign(id, obj, "id"); diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/ReactCommon/hermes/inspector/chrome/MessageTypes.h index 931e19fb41ab29..d7e38d81a49d13 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<> +// @generated SignedSource<<356df52df2a053b5254f0e039cc36a7b>> #pragma once @@ -61,6 +61,7 @@ struct InternalPropertyDescriptor; struct PropertyDescriptor; struct RemoteObject; using RemoteObjectId = std::string; +struct RunIfWaitingForDebuggerRequest; using ScriptId = std::string; struct StackTrace; using Timestamp = double; @@ -101,6 +102,7 @@ struct RequestHandler { virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; virtual void handle(const runtime::EvaluateRequest &req) = 0; virtual void handle(const runtime::GetPropertiesRequest &req) = 0; + virtual void handle(const runtime::RunIfWaitingForDebuggerRequest &req) = 0; }; /// NoopRequestHandler can be subclassed to only handle some requests. @@ -127,6 +129,7 @@ struct NoopRequestHandler : public RequestHandler { void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} void handle(const runtime::EvaluateRequest &req) override {} void handle(const runtime::GetPropertiesRequest &req) override {} + void handle(const runtime::RunIfWaitingForDebuggerRequest &req) override {} }; /// Types @@ -455,6 +458,14 @@ struct runtime::GetPropertiesRequest : public Request { folly::Optional ownProperties; }; +struct runtime::RunIfWaitingForDebuggerRequest : public Request { + RunIfWaitingForDebuggerRequest(); + explicit RunIfWaitingForDebuggerRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; +}; + /// Responses struct ErrorResponse : public Response { ErrorResponse() = default; diff --git a/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp b/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp index b55194a1869e1e..9d5abc5962a413 100644 --- a/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp +++ b/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp @@ -44,7 +44,8 @@ namespace { // the already-deallocated connection. class TestContext { public: - TestContext() : conn_(runtime_.runtime()) {} + TestContext(bool waitForDebugger = false) + : conn_(runtime_.runtime(), waitForDebugger) {} ~TestContext() { runtime_.wait(); } @@ -85,6 +86,7 @@ NotificationType expectNotification(SyncConnection &conn) { note = NotificationType(folly::parseJson(str)); } catch (const std::exception &e) { parseError = e.what(); + parseError += " (json: " + str + ")"; } EXPECT_EQ(parseError, ""); @@ -2349,6 +2351,44 @@ TEST(ConnectionTests, wontStopOnFilesWithoutSourceMaps) { expectNotification(conn); } +TEST(ConnectionTests, runIfWaitingForDebugger) { + TestContext context(true); + AsyncHermesRuntime &asyncRuntime = context.runtime(); + SyncConnection &conn = context.conn(); + int msgId = 0; + + asyncRuntime.executeScriptAsync(R"( + storeValue(1); debugger; + )"); + + send(conn, ++msgId); + expectExecutionContextCreated(conn); + expectNotification(conn); + expectNotification(conn); + + // We should now be paused on load. Verify that we didn't run code. + ASSERT_FALSE(asyncRuntime.hasStoredValue()); + + // RunIfWaitingForDebugger should cause us to resume + send(conn, ++msgId); + expectNotification(conn); + + // We should immediately hit the 'debugger;' statement + expectNotification(conn); + EXPECT_EQ(1, asyncRuntime.awaitStoredValue().asNumber()); + + // RunIfWaitingForDebuggerResponse should be accepted but have no effect + send(conn, ++msgId); + + // Do a dummy call so we can expect something other than a ResumeRequest + sendRuntimeEvalRequest(conn, ++msgId, "true"); + expectEvalResponse(conn, msgId, true); + + // Finally explicitly continue and exit + send(conn, msgId++); + expectNotification(conn); +} + } // namespace chrome } // namespace inspector } // namespace hermes diff --git a/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp b/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp index d30b044d2167e7..0514db17fe9cef 100644 --- a/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp +++ b/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp @@ -52,12 +52,15 @@ class SyncConnection::RemoteConnnection : public IRemoteConnection { SyncConnection &conn_; }; -SyncConnection::SyncConnection(std::shared_ptr runtime) +SyncConnection::SyncConnection( + std::shared_ptr runtime, + bool waitForDebugger) : connection_( std::make_unique( runtime, runtime->getDebugger()), - "testConn") { + "testConn", + waitForDebugger) { connection_.connect(std::make_unique(*this)); } diff --git a/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h b/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h index 40f97ee7d0d6e8..97da2ec8657ba6 100644 --- a/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h +++ b/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h @@ -28,7 +28,9 @@ namespace chrome { */ class SyncConnection { public: - SyncConnection(std::shared_ptr runtime); + SyncConnection( + std::shared_ptr runtime, + bool waitForDebugger = false); ~SyncConnection() = default; /// sends a message to the debugger diff --git a/ReactCommon/hermes/inspector/tools/message_types.txt b/ReactCommon/hermes/inspector/tools/message_types.txt index 0db4a6b7709ce9..02efcce5f1bc50 100644 --- a/ReactCommon/hermes/inspector/tools/message_types.txt +++ b/ReactCommon/hermes/inspector/tools/message_types.txt @@ -24,3 +24,4 @@ Runtime.consoleAPICalled Runtime.evaluate Runtime.executionContextCreated Runtime.getProperties +Runtime.runIfWaitingForDebugger diff --git a/ReactCommon/jsi/BUCK b/ReactCommon/jsi/BUCK index f03406c20331e7..d4d1e6704575d1 100644 --- a/ReactCommon/jsi/BUCK +++ b/ReactCommon/jsi/BUCK @@ -35,6 +35,7 @@ rn_xplat_cxx_library( "-Wglobal-constructors", "-Wmissing-prototypes", ], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = ["PUBLIC"], ) @@ -52,6 +53,7 @@ rn_xplat_cxx_library( "-frtti", ], fbobjc_force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], @@ -78,9 +80,9 @@ rn_xplat_cxx_library( fbobjc_frameworks = [ "$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework", ], - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], # 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"], xplat_mangled_args = { diff --git a/ReactCommon/jsi/JSCRuntime.cpp b/ReactCommon/jsi/JSCRuntime.cpp index 91f87b81f1cf7b..b6e6062847cc87 100644 --- a/ReactCommon/jsi/JSCRuntime.cpp +++ b/ReactCommon/jsi/JSCRuntime.cpp @@ -185,7 +185,7 @@ class JSCRuntime : public jsi::Runtime { // TODO: revisit this implementation jsi::WeakObject createWeakObject(const jsi::Object &) override; - jsi::Value lockWeakObject(const jsi::WeakObject &) override; + jsi::Value lockWeakObject(jsi::WeakObject &) override; jsi::Array createArray(size_t length) override; size_t size(const jsi::Array &) override; @@ -988,7 +988,7 @@ jsi::WeakObject JSCRuntime::createWeakObject(const jsi::Object &obj) { #endif } -jsi::Value JSCRuntime::lockWeakObject(const jsi::WeakObject &obj) { +jsi::Value JSCRuntime::lockWeakObject(jsi::WeakObject &obj) { #ifdef RN_FABRIC_ENABLED // TODO: revisit this implementation JSObjectRef objRef = objectRef(obj); diff --git a/ReactCommon/jsi/jsi/decorator.h b/ReactCommon/jsi/jsi/decorator.h index d090a47fc9c974..46e7414abbecb1 100644 --- a/ReactCommon/jsi/jsi/decorator.h +++ b/ReactCommon/jsi/jsi/decorator.h @@ -256,7 +256,7 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { WeakObject createWeakObject(const Object& o) override { return plain_.createWeakObject(o); }; - Value lockWeakObject(const WeakObject& wo) override { + Value lockWeakObject(WeakObject& wo) override { return plain_.lockWeakObject(wo); }; @@ -633,7 +633,7 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; return RD::createWeakObject(o); }; - Value lockWeakObject(const WeakObject& wo) override { + Value lockWeakObject(WeakObject& wo) override { Around around{with_}; return RD::lockWeakObject(wo); }; diff --git a/ReactCommon/jsi/jsi/jsi-inl.h b/ReactCommon/jsi/jsi/jsi-inl.h index 08495995663c7c..63d4a2f5868d90 100644 --- a/ReactCommon/jsi/jsi/jsi-inl.h +++ b/ReactCommon/jsi/jsi/jsi-inl.h @@ -65,6 +65,10 @@ inline T Runtime::make(Runtime::PointerValue* pv) { return T(pv); } +inline Runtime::PointerValue* Runtime::getPointerValue(jsi::Pointer& pointer) { + return pointer.ptr_; +} + inline const Runtime::PointerValue* Runtime::getPointerValue( const jsi::Pointer& pointer) { return pointer.ptr_; diff --git a/ReactCommon/jsi/jsi/jsi.h b/ReactCommon/jsi/jsi/jsi.h index e48f524a105b1d..67823488442ab8 100644 --- a/ReactCommon/jsi/jsi/jsi.h +++ b/ReactCommon/jsi/jsi/jsi.h @@ -280,7 +280,7 @@ class Runtime { virtual Array getPropertyNames(const Object&) = 0; virtual WeakObject createWeakObject(const Object&) = 0; - virtual Value lockWeakObject(const WeakObject&) = 0; + virtual Value lockWeakObject(WeakObject&) = 0; virtual Array createArray(size_t length) = 0; virtual size_t size(const Array&) = 0; @@ -316,6 +316,7 @@ class Runtime { // Value, Symbol, String, and Object, which are all friends of Runtime. template static T make(PointerValue* pv); + static PointerValue* getPointerValue(Pointer& pointer); static const PointerValue* getPointerValue(const Pointer& pointer); static const PointerValue* getPointerValue(const Value& value); diff --git a/ReactCommon/jsiexecutor/Android.mk b/ReactCommon/jsiexecutor/Android.mk index c6bb60124a0744..74ae7a65cef20e 100644 --- a/ReactCommon/jsiexecutor/Android.mk +++ b/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 +LOCAL_STATIC_LIBRARIES := libjsi reactnative reactperflogger LOCAL_SHARED_LIBRARIES := libfolly_json glog include $(BUILD_STATIC_LIBRARY) diff --git a/ReactCommon/jsiexecutor/BUCK b/ReactCommon/jsiexecutor/BUCK index 1edfe366a9ac7e..ab3a5f50d5a68c 100644 --- a/ReactCommon/jsiexecutor/BUCK +++ b/ReactCommon/jsiexecutor/BUCK @@ -20,10 +20,9 @@ cxx_library( "//xplat/folly:molly", "//xplat/third-party/linker_lib:atomic", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_force_static = True, fbobjc_header_path_prefix = "", - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID, APPLE), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", @@ -39,5 +38,6 @@ cxx_library( react_native_xplat_dep("jsi:JSIDynamic"), react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("cxxreact:jsbigstring"), + react_native_xplat_target("reactperflogger:reactperflogger"), ], ) diff --git a/ReactCommon/jsiexecutor/React-jsiexecutor.podspec b/ReactCommon/jsiexecutor/React-jsiexecutor.podspec index eef1279ac5e33f..a8ac598cbf85bd 100644 --- a/ReactCommon/jsiexecutor/React-jsiexecutor.podspec +++ b/ReactCommon/jsiexecutor/React-jsiexecutor.podspec @@ -37,6 +37,7 @@ Pod::Spec.new do |s| s.dependency "React-cxxreact", version s.dependency "React-jsi", version s.dependency "RCT-Folly", folly_version + s.dependency "React-perflogger", version s.dependency "DoubleConversion" s.dependency "glog" end diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index fd77eba63e6461..c305615250b442 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,7 @@ JSIExecutor::JSIExecutor( delegate_(delegate), nativeModules_(std::make_shared( delegate ? delegate->getModuleRegistry() : nullptr)), + moduleRegistry_(delegate ? delegate->getModuleRegistry() : nullptr), scopedTimeoutInvoker_(scopedTimeoutInvoker), runtimeInstaller_(runtimeInstaller) { runtime_->global().setProperty( @@ -383,6 +385,8 @@ void JSIExecutor::callNativeModules(const Value &queue, bool isEndOfBatch) { .getPropertyAsFunction(*runtime_, "stringify").call(*runtime_, queue) .getString(*runtime_).utf8(*runtime_); #endif + BridgeNativeModulePerfLogger::asyncMethodCallBatchPreprocessStart(); + delegate_->callNativeModules( *this, dynamicFromValue(*runtime_, queue), isEndOfBatch); } @@ -440,16 +444,54 @@ Value JSIExecutor::nativeCallSyncHook(const Value *args, size_t count) { folly::to("method parameters should be array")); } + unsigned int moduleId = static_cast(args[0].getNumber()); + unsigned int methodId = static_cast(args[1].getNumber()); + std::string moduleName; + std::string methodName; + + if (moduleRegistry_) { + moduleName = moduleRegistry_->getModuleName(moduleId); + methodName = moduleRegistry_->getModuleSyncMethodName(moduleId, methodId); + + BridgeNativeModulePerfLogger::syncMethodCallStart( + moduleName.c_str(), methodName.c_str()); + + BridgeNativeModulePerfLogger::syncMethodCallArgConversionStart( + moduleName.c_str(), methodName.c_str()); + } + MethodCallResult result = delegate_->callSerializableNativeHook( - *this, - static_cast(args[0].getNumber()), // moduleId - static_cast(args[1].getNumber()), // methodId - dynamicFromValue(*runtime_, args[2])); // args + *this, moduleId, methodId, dynamicFromValue(*runtime_, args[2])); + + /** + * Note: + * In RCTNativeModule, folly::none is returned from callSerializableNativeHook + * when executing a NativeModule method fails. Therefore, it's safe to not + * terminate the syncMethodCall when folly::none is returned. + * + * TODO: In JavaNativeModule, folly::none is returned when the synchronous + * NativeModule method has the void return type. Change this to return + * folly::dynamic(nullptr) instead, so that folly::none is reserved for + * exceptional scenarios. + * + * TODO: Investigate CxxModule infra to see if folly::none is used for + * returns in exceptional scenarios. + **/ if (!result.hasValue()) { return Value::undefined(); } - return valueFromDynamic(*runtime_, result.value()); + + Value returnValue = valueFromDynamic(*runtime_, result.value()); + + if (moduleRegistry_) { + BridgeNativeModulePerfLogger::syncMethodCallReturnConversionEnd( + moduleName.c_str(), methodName.c_str()); + BridgeNativeModulePerfLogger::syncMethodCallEnd( + moduleName.c_str(), methodName.c_str()); + } + + return returnValue; } #if DEBUG diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h index 2cd3776baf52a2..615172ed711c64 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h +++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h @@ -123,6 +123,7 @@ class JSIExecutor : public JSExecutor { std::shared_ptr runtime_; std::shared_ptr delegate_; std::shared_ptr nativeModules_; + std::shared_ptr moduleRegistry_; std::once_flag bindFlag_; std::unique_ptr bundleRegistry_; JSIScopedTimeoutInvoker scopedTimeoutInvoker_; diff --git a/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp b/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp index 425152cdac688d..e7c56c206a5f26 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp +++ b/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp @@ -6,6 +6,7 @@ */ #include "jsireact/JSINativeModules.h" +#include #include @@ -31,13 +32,21 @@ Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name) { std::string moduleName = name.utf8(rt); + BridgeNativeModulePerfLogger::moduleJSRequireBeginningStart( + moduleName.c_str()); + const auto it = m_objects.find(moduleName); if (it != m_objects.end()) { + BridgeNativeModulePerfLogger::moduleJSRequireBeginningCacheHit( + moduleName.c_str()); + BridgeNativeModulePerfLogger::moduleJSRequireBeginningEnd( + moduleName.c_str()); return Value(rt, it->second); } auto module = createModule(rt, moduleName); if (!module.hasValue()) { + BridgeNativeModulePerfLogger::moduleJSRequireEndingFail(moduleName.c_str()); // Allow lookup to continue in the objects own properties, which allows for // overrides of NativeModules return nullptr; @@ -45,7 +54,10 @@ Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name) { auto result = m_objects.emplace(std::move(moduleName), std::move(*module)).first; - return Value(rt, result->second); + + Value ret = Value(rt, result->second); + BridgeNativeModulePerfLogger::moduleJSRequireEndingEnd(moduleName.c_str()); + return ret; } void JSINativeModules::reset() { diff --git a/ReactCommon/jsinspector/BUCK b/ReactCommon/jsinspector/BUCK index 22b569eabee601..a64c399b567d72 100644 --- a/ReactCommon/jsinspector/BUCK +++ b/ReactCommon/jsinspector/BUCK @@ -30,9 +30,8 @@ rn_xplat_cxx_library( "-fexceptions", "-std=c++1y", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_preferred_linkage = "shared", - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactCommon/microprofiler/BUCK b/ReactCommon/microprofiler/BUCK index 571b3cea525be1..d01a92546fc4ac 100644 --- a/ReactCommon/microprofiler/BUCK +++ b/ReactCommon/microprofiler/BUCK @@ -16,9 +16,8 @@ cxx_library( "-fexceptions", "-fno-data-sections", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], visibility = [ "PUBLIC", ], diff --git a/ReactCommon/reactperflogger/.clang-tidy b/ReactCommon/reactperflogger/.clang-tidy new file mode 100644 index 00000000000000..c98fd78ff64baa --- /dev/null +++ b/ReactCommon/reactperflogger/.clang-tidy @@ -0,0 +1,5 @@ +--- +Checks: '> +clang-diagnostic-*, +' +... diff --git a/ReactCommon/reactperflogger/Android.mk b/ReactCommon/reactperflogger/Android.mk new file mode 100644 index 00000000000000..f2c15a8027e325 --- /dev/null +++ b/ReactCommon/reactperflogger/Android.mk @@ -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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +# Header search path for all source files in this module. +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 + +# Name of this module. +LOCAL_MODULE := reactperflogger + +# Compile all local c++ files under ./ReactCommon +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/reactperflogger/*.cpp) + +# Build the files in this directory as a shared library +include $(BUILD_STATIC_LIBRARY) diff --git a/ReactCommon/reactperflogger/BUCK b/ReactCommon/reactperflogger/BUCK new file mode 100644 index 00000000000000..9724f144272352 --- /dev/null +++ b/ReactCommon/reactperflogger/BUCK @@ -0,0 +1,28 @@ +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "rn_xplat_cxx_library") + +rn_xplat_cxx_library( + name = "reactperflogger", + srcs = glob(["**/*.cpp"]), + header_namespace = "", + exported_headers = { + "reactperflogger/BridgeNativeModulePerfLogger.h": "reactperflogger/BridgeNativeModulePerfLogger.h", + "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"], + platforms = (ANDROID, APPLE), + preferred_linkage = "static", + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + visibility = [ + "PUBLIC", + ], +) diff --git a/ReactCommon/reactperflogger/React-perflogger.podspec b/ReactCommon/reactperflogger/React-perflogger.podspec new file mode 100644 index 00000000000000..2bb69eb70c605f --- /dev/null +++ b/ReactCommon/reactperflogger/React-perflogger.podspec @@ -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. + +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 +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' +boost_compiler_flags = '-Wno-documentation' + +Pod::Spec.new do |s| + s.name = "React-perflogger" + 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 => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.source = source + s.source_files = "**/*.{cpp,h}" + s.header_dir = "reactperflogger" +end diff --git a/ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.cpp b/ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.cpp new file mode 100644 index 00000000000000..70582331c799d8 --- /dev/null +++ b/ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.cpp @@ -0,0 +1,320 @@ +/* + * 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 "BridgeNativeModulePerfLogger.h" + +namespace facebook { +namespace react { +namespace BridgeNativeModulePerfLogger { + +std::unique_ptr g_perfLogger = nullptr; + +void enableLogging(std::unique_ptr &&newPerfLogger) { + g_perfLogger = std::move(newPerfLogger); +} + +void moduleDataCreateStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleDataCreateStart(moduleName, id); + } +} + +void moduleDataCreateEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleDataCreateEnd(moduleName, id); + } +} + +void moduleCreateStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateStart(moduleName, id); + } +} + +void moduleCreateCacheHit(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateCacheHit(moduleName, id); + } +} + +void moduleCreateConstructStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateConstructStart(moduleName, id); + } +} + +void moduleCreateConstructEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateConstructEnd(moduleName, id); + } +} + +void moduleCreateSetUpStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateSetUpStart(moduleName, id); + } +} + +void moduleCreateSetUpEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateSetUpEnd(moduleName, id); + } +} + +void moduleCreateEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateEnd(moduleName, id); + } +} + +void moduleCreateFail(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateFail(moduleName, id); + } +} + +void moduleJSRequireBeginningStart(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningStart(moduleName); + } +} + +void moduleJSRequireBeginningCacheHit(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningCacheHit(moduleName); + } +} + +void moduleJSRequireBeginningEnd(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningEnd(moduleName); + } +} + +void moduleJSRequireBeginningFail(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningFail(moduleName); + } +} + +void moduleJSRequireEndingStart(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireEndingStart(moduleName); + } +} + +void moduleJSRequireEndingEnd(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireEndingEnd(moduleName); + } +} + +void moduleJSRequireEndingFail(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireEndingFail(moduleName); + } +} + +void syncMethodCallStart(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallStart(moduleName, methodName); + } +} + +void syncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallArgConversionStart(moduleName, methodName); + } +} + +void syncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallArgConversionEnd(moduleName, methodName); + } +} + +void syncMethodCallExecutionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallExecutionStart(moduleName, methodName); + } +} +void syncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallExecutionEnd(moduleName, methodName); + } +} + +void syncMethodCallReturnConversionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallReturnConversionStart(moduleName, methodName); + } +} + +void syncMethodCallReturnConversionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallReturnConversionEnd(moduleName, methodName); + } +} + +void syncMethodCallEnd(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallEnd(moduleName, methodName); + } +} + +void syncMethodCallFail(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallFail(moduleName, methodName); + } +} + +void asyncMethodCallStart(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallStart(moduleName, methodName); + } +} + +void asyncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallArgConversionStart(moduleName, methodName); + } +} + +void asyncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallArgConversionEnd(moduleName, methodName); + } +} + +void asyncMethodCallDispatch(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallDispatch(moduleName, methodName); + } +} + +void asyncMethodCallEnd(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallEnd(moduleName, methodName); + } +} + +void asyncMethodCallBatchPreprocessStart() { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallBatchPreprocessStart(); + } +} + +void asyncMethodCallBatchPreprocessEnd(int batchSize) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallBatchPreprocessEnd(batchSize); + } +} + +void asyncMethodCallExecutionStart( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionStart(moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionArgConversionStart( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionArgConversionStart( + moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionArgConversionEnd( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionArgConversionEnd( + moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionEnd(moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionFail( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionFail(moduleName, methodName, id); + } +} + +} // namespace BridgeNativeModulePerfLogger +} // namespace react +} // namespace facebook diff --git a/ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.h b/ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.h new file mode 100644 index 00000000000000..e82257abf538f6 --- /dev/null +++ b/ReactCommon/reactperflogger/reactperflogger/BridgeNativeModulePerfLogger.h @@ -0,0 +1,111 @@ +/* + * 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 "NativeModulePerfLogger.h" + +namespace facebook { +namespace react { + +namespace BridgeNativeModulePerfLogger { +void enableLogging(std::unique_ptr &&logger); + +void moduleDataCreateStart(const char *moduleName, int32_t id); +void moduleDataCreateEnd(const char *moduleName, int32_t id); + +/** + * Create NativeModule platform object + */ +void moduleCreateStart(const char *moduleName, int32_t id); +void moduleCreateCacheHit(const char *moduleName, int32_t id); +void moduleCreateConstructStart(const char *moduleName, int32_t id); +void moduleCreateConstructEnd(const char *moduleName, int32_t id); +void moduleCreateSetUpStart(const char *moduleName, int32_t id); +void moduleCreateSetUpEnd(const char *moduleName, int32_t id); +void moduleCreateEnd(const char *moduleName, int32_t id); +void moduleCreateFail(const char *moduleName, int32_t id); + +/** + * JS require beginning + */ +void moduleJSRequireBeginningStart(const char *moduleName); +void moduleJSRequireBeginningCacheHit(const char *moduleName); +void moduleJSRequireBeginningEnd(const char *moduleName); +void moduleJSRequireBeginningFail(const char *moduleName); + +/** + * JS require ending + */ +void moduleJSRequireEndingStart(const char *moduleName); +void moduleJSRequireEndingEnd(const char *moduleName); +void moduleJSRequireEndingFail(const char *moduleName); + +// Sync method calls +void syncMethodCallStart(const char *moduleName, const char *methodName); +void syncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName); +void syncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName); +void syncMethodCallExecutionStart( + const char *moduleName, + const char *methodName); +void syncMethodCallExecutionEnd(const char *moduleName, const char *methodName); +void syncMethodCallReturnConversionStart( + const char *moduleName, + const char *methodName); +void syncMethodCallReturnConversionEnd( + const char *moduleName, + const char *methodName); +void syncMethodCallEnd(const char *moduleName, const char *methodName); +void syncMethodCallFail(const char *moduleName, const char *methodName); + +// Async method calls +void asyncMethodCallStart(const char *moduleName, const char *methodName); +void asyncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName); +void asyncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName); +void asyncMethodCallDispatch(const char *moduleName, const char *methodName); +void asyncMethodCallEnd(const char *moduleName, const char *methodName); + +/** + * Pre-processing async method call batch + */ +void asyncMethodCallBatchPreprocessStart(); +void asyncMethodCallBatchPreprocessEnd(int batchSize); + +// Async method call execution +void asyncMethodCallExecutionStart( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionArgConversionStart( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionArgConversionEnd( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionFail( + const char *moduleName, + const char *methodName, + int32_t id); + +} // namespace BridgeNativeModulePerfLogger + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/reactperflogger/reactperflogger/NativeModulePerfLogger.h b/ReactCommon/reactperflogger/reactperflogger/NativeModulePerfLogger.h new file mode 100644 index 00000000000000..f4e9591e0c439f --- /dev/null +++ b/ReactCommon/reactperflogger/reactperflogger/NativeModulePerfLogger.h @@ -0,0 +1,161 @@ +/* + * 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 { + +/** + * A platform-agnostic interface to do performance logging on NativeModules and + * TuboModules. + */ +class NativeModulePerfLogger { + public: + virtual ~NativeModulePerfLogger() {} + + /** + * NativeModule Initialization. + * + * The initialization of two NativeModules can interleave. Therefore, + * performance markers should use the moduleName as a unique key. + */ + + /** + * On iOS: + * - NativeModule initialization is split into two phases, which sometimes + * have a pause in the middle. + * - TurboModule initialization happens all at once. + * + * On Android: + * - NativeModule and TurboModule initialization happens all at once. + * + * These markers are meant for iOS NativeModules: + * - moduleDataCreateStart: very beginning of first phase. + * - moduleDataCreateEnd: after RCTModuleData has been created. + */ + virtual void moduleDataCreateStart(const char *moduleName, int32_t id) = 0; + virtual void moduleDataCreateEnd(const char *moduleName, int32_t id) = 0; + + /** + * How long does it take to create the platform NativeModule object? + * - moduleCreateStart: start creating platform NativeModule + * - moduleCreateEnd: stop creating platform NativeModule + */ + virtual void moduleCreateStart(const char *moduleName, int32_t id) = 0; + virtual void moduleCreateCacheHit(const char *moduleName, int32_t id) = 0; + virtual void moduleCreateConstructStart( + const char *moduleName, + int32_t id) = 0; + virtual void moduleCreateConstructEnd(const char *moduleName, int32_t id) = 0; + virtual void moduleCreateSetUpStart(const char *moduleName, int32_t id) = 0; + virtual void moduleCreateSetUpEnd(const char *moduleName, int32_t id) = 0; + virtual void moduleCreateEnd(const char *moduleName, int32_t id) = 0; + virtual void moduleCreateFail(const char *moduleName, int32_t id) = 0; + + /** + * How long, after starting JS require, does it take to start creating the + * platform NativeModule? + * - moduleJSRequireBeginningStart: start of JS require + * - moduleJSRequireBeginningEnd: start creating platform NativeModule + */ + virtual void moduleJSRequireBeginningStart(const char *moduleName) = 0; + virtual void moduleJSRequireBeginningCacheHit(const char *moduleName) = 0; + virtual void moduleJSRequireBeginningEnd(const char *moduleName) = 0; + virtual void moduleJSRequireBeginningFail(const char *moduleName) = 0; + + /** + * How long does it take to return from the JS require after the platform + * NativeModule is created? + * - moduleJSRequireEndingStart: end creating platform NativeModule + * - moduleJSRequireEndingEnd: end of JS require + */ + virtual void moduleJSRequireEndingStart(const char *moduleName) = 0; + virtual void moduleJSRequireEndingEnd(const char *moduleName) = 0; + virtual void moduleJSRequireEndingFail(const char *moduleName) = 0; + + // Sync method calls + virtual void syncMethodCallStart( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallExecutionStart( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallReturnConversionStart( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallReturnConversionEnd( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallEnd( + const char *moduleName, + const char *methodName) = 0; + virtual void syncMethodCallFail( + const char *moduleName, + const char *methodName) = 0; + + // Async method calls + virtual void asyncMethodCallStart( + const char *moduleName, + const char *methodName) = 0; + virtual void asyncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName) = 0; + virtual void asyncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName) = 0; + virtual void asyncMethodCallDispatch( + const char *moduleName, + const char *methodName) = 0; + virtual void asyncMethodCallEnd( + const char *moduleName, + const char *methodName) = 0; + + /** + * In the NativeModule system, we batch async NativeModule method calls. + * When we execute a batch of NativeModule method calls, we convert the batch + * from a jsi::Value to folly::dynamic to std::vector. This marker + * documents that work. + */ + virtual void asyncMethodCallBatchPreprocessStart() = 0; + virtual void asyncMethodCallBatchPreprocessEnd(int batchSize) = 0; + + // Async method call execution + virtual void asyncMethodCallExecutionStart( + const char *moduleName, + const char *methodName, + int32_t id) = 0; + virtual void asyncMethodCallExecutionArgConversionStart( + const char *moduleName, + const char *methodName, + int32_t id) = 0; + virtual void asyncMethodCallExecutionArgConversionEnd( + const char *moduleName, + const char *methodName, + int32_t id) = 0; + virtual void asyncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName, + int32_t id) = 0; + virtual void asyncMethodCallExecutionFail( + const char *moduleName, + const char *methodName, + int32_t id) = 0; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/runtimeexecutor/BUCK b/ReactCommon/runtimeexecutor/BUCK index 561b4c8d208b49..08ee7f4c232722 100644 --- a/ReactCommon/runtimeexecutor/BUCK +++ b/ReactCommon/runtimeexecutor/BUCK @@ -36,9 +36,9 @@ rn_xplat_cxx_library( ], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_frameworks = ["Foundation"], - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h b/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h index e01e2df41c1da8..90139a6a99cfea 100644 --- a/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h +++ b/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h @@ -48,7 +48,7 @@ inline static void executeAsynchronously( * Executes a `callback` in a *synchronous* manner using given * `RuntimeExecutor`. * Use this method when the caller needs to *be blocked* by executing the - * callback but does not concerted about the particular thread on which the + * callback but does not concerned about the particular thread on which the * `callback` will be executed. */ inline static void executeSynchronously_CAN_DEADLOCK( @@ -89,8 +89,18 @@ inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK( jsi::Runtime *runtimePtr; + auto threadId = std::this_thread::get_id(); + runtimeExecutor([&](jsi::Runtime &runtime) { runtimePtr = &runtime; + + if (threadId == std::this_thread::get_id()) { + // In case of a synchronous call, we should unlock mutexes and return. + mutex1.unlock(); + mutex3.unlock(); + return; + } + mutex1.unlock(); // `callback` is called somewhere here. mutex2.lock(); diff --git a/ReactCommon/turbomodule/core/BUCK b/ReactCommon/turbomodule/core/BUCK index af7350e7f2ccad..512c16ef811fe2 100644 --- a/ReactCommon/turbomodule/core/BUCK +++ b/ReactCommon/turbomodule/core/BUCK @@ -18,6 +18,7 @@ rn_xplat_cxx_library( "-frtti", "-std=c++14", "-Wall", + "-Wno-global-constructors", ], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), @@ -28,7 +29,6 @@ rn_xplat_cxx_library( ], prefix = "ReactCommon", ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( [ "platform/android/**/*.cpp", @@ -39,7 +39,6 @@ rn_xplat_cxx_library( "-fobjc-arc-exceptions", ], fbobjc_inherited_buck_flags = get_static_library_ios_flags(), - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode(), force_static = True, ios_deps = [ @@ -62,6 +61,7 @@ rn_xplat_cxx_library( "platform/ios/**/*.mm", ], ), + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID, APPLE), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", @@ -80,6 +80,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("reactperflogger:reactperflogger"), ], exported_deps = [ "//xplat/jsi:jsi", diff --git a/ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp b/ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp new file mode 100644 index 00000000000000..5d145e1b869b39 --- /dev/null +++ b/ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp @@ -0,0 +1,320 @@ +/* + * 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 "TurboModulePerfLogger.h" + +namespace facebook { +namespace react { +namespace TurboModulePerfLogger { + +std::unique_ptr g_perfLogger = nullptr; + +void enableLogging(std::unique_ptr &&newPerfLogger) { + g_perfLogger = std::move(newPerfLogger); +} + +void moduleDataCreateStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleDataCreateStart(moduleName, id); + } +} + +void moduleDataCreateEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleDataCreateEnd(moduleName, id); + } +} + +void moduleCreateStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateStart(moduleName, id); + } +} + +void moduleCreateCacheHit(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateCacheHit(moduleName, id); + } +} + +void moduleCreateConstructStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateConstructStart(moduleName, id); + } +} + +void moduleCreateConstructEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateConstructEnd(moduleName, id); + } +} + +void moduleCreateSetUpStart(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateSetUpStart(moduleName, id); + } +} + +void moduleCreateSetUpEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateSetUpEnd(moduleName, id); + } +} + +void moduleCreateEnd(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateEnd(moduleName, id); + } +} + +void moduleCreateFail(const char *moduleName, int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleCreateFail(moduleName, id); + } +} + +void moduleJSRequireBeginningStart(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningStart(moduleName); + } +} + +void moduleJSRequireBeginningCacheHit(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningCacheHit(moduleName); + } +} + +void moduleJSRequireBeginningEnd(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningEnd(moduleName); + } +} + +void moduleJSRequireBeginningFail(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireBeginningFail(moduleName); + } +} + +void moduleJSRequireEndingStart(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireEndingStart(moduleName); + } +} + +void moduleJSRequireEndingEnd(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireEndingEnd(moduleName); + } +} + +void moduleJSRequireEndingFail(const char *moduleName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->moduleJSRequireEndingFail(moduleName); + } +} + +void syncMethodCallStart(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallStart(moduleName, methodName); + } +} + +void syncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallArgConversionStart(moduleName, methodName); + } +} + +void syncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallArgConversionEnd(moduleName, methodName); + } +} + +void syncMethodCallExecutionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallExecutionStart(moduleName, methodName); + } +} +void syncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallExecutionEnd(moduleName, methodName); + } +} + +void syncMethodCallReturnConversionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallReturnConversionStart(moduleName, methodName); + } +} + +void syncMethodCallReturnConversionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallReturnConversionEnd(moduleName, methodName); + } +} + +void syncMethodCallEnd(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallEnd(moduleName, methodName); + } +} + +void syncMethodCallFail(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->syncMethodCallFail(moduleName, methodName); + } +} + +void asyncMethodCallStart(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallStart(moduleName, methodName); + } +} + +void asyncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallArgConversionStart(moduleName, methodName); + } +} + +void asyncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallArgConversionEnd(moduleName, methodName); + } +} + +void asyncMethodCallDispatch(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallDispatch(moduleName, methodName); + } +} + +void asyncMethodCallEnd(const char *moduleName, const char *methodName) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallEnd(moduleName, methodName); + } +} + +void asyncMethodCallBatchPreprocessStart() { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallBatchPreprocessStart(); + } +} + +void asyncMethodCallBatchPreprocessEnd(int batchSize) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallBatchPreprocessEnd(batchSize); + } +} + +void asyncMethodCallExecutionStart( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionStart(moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionArgConversionStart( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionArgConversionStart( + moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionArgConversionEnd( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionArgConversionEnd( + moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionEnd(moduleName, methodName, id); + } +} + +void asyncMethodCallExecutionFail( + const char *moduleName, + const char *methodName, + int32_t id) { + NativeModulePerfLogger *logger = g_perfLogger.get(); + if (logger != nullptr) { + logger->asyncMethodCallExecutionFail(moduleName, methodName, id); + } +} + +} // namespace TurboModulePerfLogger +} // namespace react +} // namespace facebook diff --git a/ReactCommon/turbomodule/core/TurboModulePerfLogger.h b/ReactCommon/turbomodule/core/TurboModulePerfLogger.h new file mode 100644 index 00000000000000..fe3a8ab30d4716 --- /dev/null +++ b/ReactCommon/turbomodule/core/TurboModulePerfLogger.h @@ -0,0 +1,110 @@ +/* + * 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 { +namespace TurboModulePerfLogger { +void enableLogging(std::unique_ptr &&logger); + +void moduleDataCreateStart(const char *moduleName, int32_t id); +void moduleDataCreateEnd(const char *moduleName, int32_t id); + +/** + * Create NativeModule platform object + */ +void moduleCreateStart(const char *moduleName, int32_t id); +void moduleCreateCacheHit(const char *moduleName, int32_t id); +void moduleCreateConstructStart(const char *moduleName, int32_t id); +void moduleCreateConstructEnd(const char *moduleName, int32_t id); +void moduleCreateSetUpStart(const char *moduleName, int32_t id); +void moduleCreateSetUpEnd(const char *moduleName, int32_t id); +void moduleCreateEnd(const char *moduleName, int32_t id); +void moduleCreateFail(const char *moduleName, int32_t id); + +/** + * JS require beginning + */ +void moduleJSRequireBeginningStart(const char *moduleName); +void moduleJSRequireBeginningCacheHit(const char *moduleName); +void moduleJSRequireBeginningEnd(const char *moduleName); +void moduleJSRequireBeginningFail(const char *moduleName); + +/** + * JS require ending + */ +void moduleJSRequireEndingStart(const char *moduleName); +void moduleJSRequireEndingEnd(const char *moduleName); +void moduleJSRequireEndingFail(const char *moduleName); + +// Sync method calls +void syncMethodCallStart(const char *moduleName, const char *methodName); +void syncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName); +void syncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName); +void syncMethodCallExecutionStart( + const char *moduleName, + const char *methodName); +void syncMethodCallExecutionEnd(const char *moduleName, const char *methodName); +void syncMethodCallReturnConversionStart( + const char *moduleName, + const char *methodName); +void syncMethodCallReturnConversionEnd( + const char *moduleName, + const char *methodName); +void syncMethodCallEnd(const char *moduleName, const char *methodName); +void syncMethodCallFail(const char *moduleName, const char *methodName); + +// Async method calls +void asyncMethodCallStart(const char *moduleName, const char *methodName); +void asyncMethodCallArgConversionStart( + const char *moduleName, + const char *methodName); +void asyncMethodCallArgConversionEnd( + const char *moduleName, + const char *methodName); +void asyncMethodCallDispatch(const char *moduleName, const char *methodName); +void asyncMethodCallEnd(const char *moduleName, const char *methodName); + +/** + * Pre-processing async method call batch + */ +void asyncMethodCallBatchPreprocessStart(); +void asyncMethodCallBatchPreprocessEnd(int batchSize); + +// Async method call execution +void asyncMethodCallExecutionStart( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionArgConversionStart( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionArgConversionEnd( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionEnd( + const char *moduleName, + const char *methodName, + int32_t id); +void asyncMethodCallExecutionFail( + const char *moduleName, + const char *methodName, + int32_t id); + +} // namespace TurboModulePerfLogger +} // namespace react +} // namespace facebook diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h index 89aa23832b8225..42dfd285928a49 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +#pragma once + #import #import @@ -22,101 +24,6 @@ ((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)])) #define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class]) -typedef int MethodCallId; - -/** - * This interface exists to allow the application to collect performance - * metrics of the TurboModule system. By implementing each function, you can - * hook into various stages of TurboModule creation and method dispatch (both async and sync). - * - * Note: - * - TurboModule async method invocations can interleave, so methodCallId should be used as a unique id for a method - * call. - */ -@protocol RCTTurboModulePerformanceLogger -// Create TurboModule JS Object -- (void)createTurboModuleStart:(const char *)moduleName; -- (void)createTurboModuleEnd:(const char *)moduleName; -- (void)createTurboModuleCacheHit:(const char *)moduleName; -- (void)getCppTurboModuleFromTMMDelegateStart:(const char *)moduleName; -- (void)getCppTurboModuleFromTMMDelegateEnd:(const char *)moduleName; -- (void)getTurboModuleFromRCTTurboModuleStart:(const char *)moduleName; -- (void)getTurboModuleFromRCTTurboModuleEnd:(const char *)moduleName; -- (void)getTurboModuleFromRCTCxxModuleStart:(const char *)moduleName; -- (void)getTurboModuleFromRCTCxxModuleEnd:(const char *)moduleName; -- (void)getTurboModuleFromTMMDelegateStart:(const char *)moduleName; -- (void)getTurboModuleFromTMMDelegateEnd:(const char *)moduleName; - -// Create RCTTurboModule object -- (void)createRCTTurboModuleStart:(const char *)moduleName; -- (void)createRCTTurboModuleEnd:(const char *)moduleName; -- (void)createRCTTurboModuleCacheHit:(const char *)moduleName; -- (void)getRCTTurboModuleClassStart:(const char *)moduleName; -- (void)getRCTTurboModuleClassEnd:(const char *)moduleName; -- (void)getRCTTurboModuleInstanceStart:(const char *)moduleName; -- (void)getRCTTurboModuleInstanceEnd:(const char *)moduleName; -- (void)setupRCTTurboModuleDispatch:(const char *)moduleName; -- (void)setupRCTTurboModuleStart:(const char *)moduleName; -- (void)setupRCTTurboModuleEnd:(const char *)moduleName; -- (void)attachRCTBridgeToRCTTurboModuleStart:(const char *)moduleName; -- (void)attachRCTBridgeToRCTTurboModuleEnd:(const char *)moduleName; -- (void)attachMethodQueueToRCTTurboModuleStart:(const char *)moduleName; -- (void)attachMethodQueueToRCTTurboModuleEnd:(const char *)moduleName; -- (void)registerRCTTurboModuleForFrameUpdatesStart:(const char *)moduleName; -- (void)registerRCTTurboModuleForFrameUpdatesEnd:(const char *)moduleName; -- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:(const char *)moduleName; -- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:(const char *)moduleName; - -// Sync method invocation -- (void)syncMethodCallStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncMethodCallEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncMethodCallArgumentConversionStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncMethodCallArgumentConversionEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncRCTTurboModuleMethodCallStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncRCTTurboModuleMethodCallEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncMethodCallReturnConversionStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)syncMethodCallReturnConversionEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; - -// Async method invocation -- (void)asyncMethodCallStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)asyncMethodCallEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)asyncMethodCallArgumentConversionStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)asyncMethodCallArgumentConversionEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)asyncRCTTurboModuleMethodCallDispatch:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)asyncRCTTurboModuleMethodCallStart:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -- (void)asyncRCTTurboModuleMethodCallEnd:(const char *)moduleName - methodName:(const char *)methodName - methodCallId:(MethodCallId)methodCallId; -@end - namespace facebook { namespace react { @@ -133,7 +40,8 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { id instance; std::shared_ptr jsInvoker; std::shared_ptr nativeInvoker; - id perfLogger; + // Does the NativeModule dispatch async methods to the JS thread? + bool isSyncModule; }; ObjCTurboModule(const InitParams ¶ms); @@ -159,17 +67,9 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { */ NSMutableDictionary *methodArgConversionSelectors_; NSDictionary *> *methodArgumentTypeNames_; + const bool isSyncModule_; + bool isMethodSync(TurboModuleMethodValueKind returnType); NSString *getArgumentTypeName(NSString *methodName, int argIndex); - id performanceLogger_; - - /** - * Required for performance logging async method invocations. - * This field is static because two nth async method calls from different - * TurboModules can interleave, and should therefore be treated as two distinct calls. - */ - static MethodCallId methodCallId_; - - static MethodCallId getNewMethodCallId(); NSInvocation *getMethodInvocation( jsi::Runtime &runtime, @@ -178,15 +78,13 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { SEL selector, const jsi::Value *args, size_t count, - NSMutableArray *retainedObjectsForInvocation, - MethodCallId methodCallId); + NSMutableArray *retainedObjectsForInvocation); jsi::Value performMethodInvocation( jsi::Runtime &runtime, TurboModuleMethodValueKind returnType, const char *methodName, NSInvocation *inv, - NSMutableArray *retainedObjectsForInvocation, - MethodCallId methodCallId); + NSMutableArray *retainedObjectsForInvocation); BOOL hasMethodArgConversionSelector(NSString *methodName, int argIndex); SEL getMethodArgConversionSelector(NSString *methodName, int argIndex); diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm index 221bff76829cb0..b44010d0304fce 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm @@ -9,6 +9,7 @@ #import #import +#import #import #import @@ -21,9 +22,16 @@ #import #import #import +#import #import -using namespace facebook; +using namespace facebook::react; + +static int32_t getUniqueId() +{ + static int32_t counter = 0; + return counter++; +} /** * All static helper functions are ObjC++ specific. @@ -90,17 +98,15 @@ 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); 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) +convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr jsInvoker) { size_t size = value.size(runtime); NSMutableArray *result = [NSMutableArray new]; @@ -112,10 +118,8 @@ static id convertJSIValueToObjCObject( 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) { jsi::Array propertyNames = value.getPropertyNames(runtime); size_t size = propertyNames.size(runtime); @@ -131,14 +135,10 @@ static id convertJSIValueToObjCObject( 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); +static id +convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr jsInvoker) { if (value.isUndefined() || value.isNull()) { return nil; @@ -166,12 +166,10 @@ static id convertJSIValueToObjCObject( 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) { - auto weakWrapper = react::CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker); + auto weakWrapper = CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker); BOOL __block wrapperWasCalled = NO; return ^(NSArray *responses) { if (wrapperWasCalled) { @@ -203,7 +201,7 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( jsi::Value ObjCTurboModule::createPromise( jsi::Runtime &runtime, - std::shared_ptr jsInvoker, + std::shared_ptr jsInvoker, PromiseInvocationBlock invoke) { if (!invoke) { @@ -228,10 +226,8 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( return jsi::Value::undefined(); } - auto weakResolveWrapper = - react::CallbackWrapper::createWeak(args[0].getObject(rt).getFunction(rt), rt, jsInvoker); - auto weakRejectWrapper = - react::CallbackWrapper::createWeak(args[1].getObject(rt).getFunction(rt), rt, jsInvoker); + auto weakResolveWrapper = CallbackWrapper::createWeak(args[0].getObject(rt).getFunction(rt), rt, jsInvoker); + auto weakRejectWrapper = CallbackWrapper::createWeak(args[1].getObject(rt).getFunction(rt), rt, jsInvoker); __block BOOL resolveWasCalled = NO; __block BOOL rejectWasCalled = NO; @@ -324,15 +320,15 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( TurboModuleMethodValueKind returnType, const char *methodName, NSInvocation *inv, - NSMutableArray *retainedObjectsForInvocation, - MethodCallId methodCallId) + NSMutableArray *retainedObjectsForInvocation) { __block id result; jsi::Runtime *rt = &runtime; __weak id weakModule = instance_; - id performanceLogger = performanceLogger_; const char *moduleName = name_.c_str(); - const bool isSync = returnType != VoidKind && returnType != PromiseKind; + std::string methodNameStr{methodName}; + __block int32_t asyncCallCounter = 0; + bool wasMethodSync = isMethodSync(returnType); void (^block)() = ^{ if (!weakModule) { @@ -341,40 +337,43 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( id strongModule = weakModule; - if (isSync) { - [performanceLogger syncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + if (wasMethodSync) { + TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str()); } else { - [performanceLogger asyncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodNameStr.c_str(), asyncCallCounter); } + // TODO(T66699874) Should we guard this with a try/catch? [inv invokeWithTarget:strongModule]; [retainedObjectsForInvocation removeAllObjects]; - if (returnType == VoidKind) { - [performanceLogger asyncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; + if (!wasMethodSync) { + TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodNameStr.c_str(), asyncCallCounter); return; } + + TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodNameStr.c_str()); + TurboModulePerfLogger::syncMethodCallReturnConversionStart(moduleName, methodNameStr.c_str()); + void *rawResult; [inv getReturnValue:&rawResult]; result = (__bridge id)rawResult; - [performanceLogger syncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; }; - if (returnType == VoidKind) { - nativeInvoker_->invokeAsync([block]() -> void { block(); }); - } else { + if (wasMethodSync) { nativeInvoker_->invokeSync([block]() -> void { block(); }); + } else { + asyncCallCounter = getUniqueId(); + TurboModulePerfLogger::asyncMethodCallDispatch(moduleName, methodName); + nativeInvoker_->invokeAsync([block]() -> void { block(); }); + return jsi::Value::undefined(); } - // VoidKind can't be null - // PromiseKind, and FunctionKind must throw errors always - if (returnType != VoidKind && returnType != PromiseKind && returnType != FunctionKind && - (result == (id)kCFNull || result == nil)) { + if (result == (id)kCFNull || result == nil) { return jsi::Value::null(); } jsi::Value returnValue = jsi::Value::undefined(); - [performanceLogger_ syncMethodCallReturnConversionStart:moduleName methodName:methodName methodCallId:methodCallId]; // TODO: Re-use value conversion logic from existing impl, if possible. switch (returnType) { @@ -407,7 +406,7 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( throw std::runtime_error("convertInvocationResultToJSIValue: PromiseKind wasn't handled properly."); } - [performanceLogger_ syncMethodCallReturnConversionEnd:moduleName methodName:methodName methodCallId:methodCallId]; + TurboModulePerfLogger::syncMethodCallReturnConversionEnd(moduleName, methodName); return returnValue; } @@ -476,21 +475,15 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( SEL selector, const jsi::Value *args, size_t count, - NSMutableArray *retainedObjectsForInvocation, - MethodCallId methodCallId) + NSMutableArray *retainedObjectsForInvocation) { - const bool isSync = returnType != VoidKind && returnType != PromiseKind; const char *moduleName = name_.c_str(); const id module = instance_; - if (isSync) { - [performanceLogger_ syncMethodCallArgumentConversionStart:moduleName - methodName:methodName - methodCallId:methodCallId]; + if (isMethodSync(returnType)) { + TurboModulePerfLogger::syncMethodCallArgConversionStart(moduleName, methodName); } else { - [performanceLogger_ asyncMethodCallArgumentConversionStart:moduleName - methodName:methodName - methodCallId:methodCallId]; + TurboModulePerfLogger::asyncMethodCallArgConversionStart(moduleName, methodName); } NSInvocation *inv = @@ -594,32 +587,28 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( } } - if (isSync) { - [performanceLogger_ syncMethodCallArgumentConversionEnd:moduleName methodName:methodName methodCallId:methodCallId]; + if (isMethodSync(returnType)) { + TurboModulePerfLogger::syncMethodCallArgConversionEnd(moduleName, methodName); } else { - [performanceLogger_ asyncMethodCallArgumentConversionEnd:moduleName - methodName:methodName - methodCallId:methodCallId]; + TurboModulePerfLogger::asyncMethodCallArgConversionEnd(moduleName, methodName); } return inv; } +bool ObjCTurboModule::isMethodSync(TurboModuleMethodValueKind returnType) +{ + return isSyncModule_ || !(returnType == VoidKind || returnType == PromiseKind); +} + ObjCTurboModule::ObjCTurboModule(const InitParams ¶ms) : TurboModule(params.moduleName, params.jsInvoker), instance_(params.instance), nativeInvoker_(params.nativeInvoker), - performanceLogger_(params.perfLogger) + isSyncModule_(params.isSyncModule) { } -MethodCallId ObjCTurboModule::methodCallId_{0}; - -MethodCallId ObjCTurboModule::getNewMethodCallId() -{ - return methodCallId_++; -} - jsi::Value ObjCTurboModule::invokeObjCMethod( jsi::Runtime &runtime, TurboModuleMethodValueKind returnType, @@ -628,20 +617,18 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( const jsi::Value *args, size_t count) { - MethodCallId methodCallId = getNewMethodCallId(); - const bool isSync = returnType != VoidKind && returnType != PromiseKind; const char *moduleName = name_.c_str(); const char *methodName = methodNameStr.c_str(); - if (isSync) { - [performanceLogger_ syncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + if (isMethodSync(returnType)) { + TurboModulePerfLogger::syncMethodCallStart(moduleName, methodName); } else { - [performanceLogger_ asyncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + TurboModulePerfLogger::asyncMethodCallStart(moduleName, methodName); } NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2]; - NSInvocation *inv = getMethodInvocation( - runtime, returnType, methodName, selector, args, count, retainedObjectsForInvocation, methodCallId); + NSInvocation *inv = + getMethodInvocation(runtime, returnType, methodName, selector, args, count, retainedObjectsForInvocation); jsi::Value returnValue = returnType == PromiseKind ? createPromise( @@ -653,14 +640,14 @@ static RCTResponseSenderBlock convertJSIFunctionToCallback( [retainedObjectsForInvocation addObject:resolveBlock]; [retainedObjectsForInvocation addObject:rejectBlock]; // The return type becomes void in the ObjC side. - performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation, methodCallId); + performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation); }) - : performMethodInvocation(runtime, returnType, methodName, inv, retainedObjectsForInvocation, methodCallId); + : performMethodInvocation(runtime, returnType, methodName, inv, retainedObjectsForInvocation); - if (isSync) { - [performanceLogger_ syncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; + if (isMethodSync(returnType)) { + TurboModulePerfLogger::syncMethodCallEnd(moduleName, methodName); } else { - [performanceLogger_ asyncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; + TurboModulePerfLogger::asyncMethodCallEnd(moduleName, methodName); } return returnValue; diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h index 870d1a7d8a2ec8..87c0c96f07e10f 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h @@ -5,6 +5,10 @@ * LICENSE file in the root directory of this source tree. */ +#pragma once + +#import + #import "RCTTurboModule.h" @protocol RCTTurboModuleManagerDelegate @@ -41,11 +45,6 @@ delegate:(id)delegate jsInvoker:(std::shared_ptr)jsInvoker; -- (instancetype)initWithBridge:(RCTBridge *)bridge - delegate:(id)delegate - jsInvoker:(std::shared_ptr)jsInvoker - performanceLogger:(id)performanceLogger; - - (void)installJSBindingWithRuntime:(facebook::jsi::Runtime *)runtime; - (void)invalidate; diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm index f6179e0d344995..accd2a2fd4e622 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm @@ -23,8 +23,9 @@ #import #import #import +#import -using namespace facebook; +using namespace facebook::react; /** * A global variable whose address we use to associate method queues to id objects. @@ -32,8 +33,15 @@ static char kAssociatedMethodQueueKey; namespace { +int32_t getUniqueId() +{ + static std::atomic counter{0}; + return counter++; +} + class TurboModuleHolder { private: + const int32_t moduleId_; id module_; bool isTryingToCreateModule_; bool isDoneCreatingModule_; @@ -41,6 +49,16 @@ std::condition_variable cv_; public: + TurboModuleHolder() + : moduleId_(getUniqueId()), module_(nil), isTryingToCreateModule_(false), isDoneCreatingModule_(false) + { + } + + int32_t getModuleId() const + { + return moduleId_; + } + void setModule(id module) { module_ = module; @@ -83,7 +101,7 @@ bool isCreatingModule() const } }; -class MethodQueueNativeCallInvoker : public facebook::react::CallInvoker { +class MethodQueueNativeCallInvoker : public CallInvoker { private: dispatch_queue_t methodQueue_; @@ -130,8 +148,7 @@ static Class getFallbackClassFromName(const char *name) @implementation RCTTurboModuleManager { jsi::Runtime *_runtime; - std::shared_ptr _jsInvoker; - id _performanceLogger; + std::shared_ptr _jsInvoker; __weak id _delegate; __weak RCTBridge *_bridge; @@ -142,11 +159,10 @@ @implementation RCTTurboModuleManager { * they want to be long-lived or short-lived. * * All instances of TurboModuleHolder are owned by the _turboModuleHolders map. - * We create TurboModuleHolder via operator[] inside getOrCreateTurboModuleHolder(). - * Henceforth, we only refer to TurboModuleHolders via pointers to entries in the _turboModuleHolders map. + * We only reference TurboModuleHolders via pointers to entries in the _turboModuleHolders map. */ std::unordered_map _turboModuleHolders; - std::unordered_map> _turboModuleCache; + std::unordered_map> _turboModuleCache; // Enforce synchronous access into _delegate std::mutex _turboModuleManagerDelegateMutex; @@ -158,22 +174,13 @@ @implementation RCTTurboModuleManager { - (instancetype)initWithBridge:(RCTBridge *)bridge delegate:(id)delegate - jsInvoker:(std::shared_ptr)jsInvoker -{ - return [self initWithBridge:bridge delegate:delegate jsInvoker:jsInvoker performanceLogger:nil]; -} - -- (instancetype)initWithBridge:(RCTBridge *)bridge - delegate:(id)delegate - jsInvoker:(std::shared_ptr)jsInvoker - performanceLogger:(id)performanceLogger + jsInvoker:(std::shared_ptr)jsInvoker { if (self = [super init]) { _jsInvoker = jsInvoker; _delegate = delegate; _bridge = bridge; _invalidating = false; - _performanceLogger = performanceLogger; // Necessary to allow NativeModules to lookup TurboModules [bridge setRCTTurboModuleLookupDelegate:self]; @@ -213,34 +220,40 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name * (for now). */ -- (std::shared_ptr)provideTurboModule:(const char *)moduleName +- (std::shared_ptr)provideTurboModule:(const char *)moduleName { auto turboModuleLookup = _turboModuleCache.find(moduleName); if (turboModuleLookup != _turboModuleCache.end()) { - [_performanceLogger createTurboModuleCacheHit:moduleName]; + TurboModulePerfLogger::moduleJSRequireBeginningCacheHit(moduleName); + TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName); return turboModuleLookup->second; } + TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName); + /** * Step 1: Look for pure C++ modules. * Pure C++ modules get priority. */ if ([_delegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) { - [_performanceLogger getCppTurboModuleFromTMMDelegateStart:moduleName]; + int32_t moduleId = getUniqueId(); + TurboModulePerfLogger::moduleCreateStart(moduleName, moduleId); auto turboModule = [_delegate getTurboModule:moduleName jsInvoker:_jsInvoker]; - [_performanceLogger getCppTurboModuleFromTMMDelegateEnd:moduleName]; if (turboModule != nullptr) { _turboModuleCache.insert({moduleName, turboModule}); + TurboModulePerfLogger::moduleCreateEnd(moduleName, moduleId); return turboModule; } + + TurboModulePerfLogger::moduleCreateFail(moduleName, moduleId); } /** * Step 2: Look for platform-specific modules. */ - [_performanceLogger createRCTTurboModuleStart:moduleName]; id module = [self provideRCTTurboModule:moduleName]; - [_performanceLogger createRCTTurboModuleEnd:moduleName]; + + TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName); // If we request that a TurboModule be created, its respective ObjC class must exist // If the class doesn't exist, then provideRCTTurboModule returns nil @@ -255,8 +268,7 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name /** * Step 2c: Create and native CallInvoker from the TurboModule's method queue. */ - std::shared_ptr nativeInvoker = - std::make_shared(methodQueue); + std::shared_ptr nativeInvoker = std::make_shared(methodQueue); /** * Have RCTCxxBridge decorate native CallInvoker, so that it's aware of TurboModule async method calls. @@ -266,20 +278,18 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name nativeInvoker = [_bridge decorateNativeCallInvoker:nativeInvoker]; } - facebook::react::ObjCTurboModule::InitParams params = { + ObjCTurboModule::InitParams params = { .moduleName = moduleName, .instance = module, .jsInvoker = _jsInvoker, .nativeInvoker = nativeInvoker, - .perfLogger = _performanceLogger, + .isSyncModule = methodQueue == RCTJSThread, }; // If RCTTurboModule supports creating its own C++ TurboModule object, // allow it to do so. if ([module respondsToSelector:@selector(getTurboModule:)]) { - [_performanceLogger getTurboModuleFromRCTTurboModuleStart:moduleName]; auto turboModule = [module getTurboModule:params]; - [_performanceLogger getTurboModuleFromRCTTurboModuleEnd:moduleName]; assert(turboModule != nullptr); _turboModuleCache.insert({moduleName, turboModule}); return turboModule; @@ -292,9 +302,7 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) { // Use TurboCxxModule compat class to wrap the CxxModule instance. // This is only for migration convenience, despite less performant. - [_performanceLogger getTurboModuleFromRCTCxxModuleStart:moduleName]; - auto turboModule = std::make_shared([((RCTCxxModule *)module) createModule], _jsInvoker); - [_performanceLogger getTurboModuleFromRCTCxxModuleEnd:moduleName]; + auto turboModule = std::make_shared([((RCTCxxModule *)module) createModule], _jsInvoker); _turboModuleCache.insert({moduleName, turboModule}); return turboModule; } @@ -302,9 +310,7 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name /** * Step 2e: Return an exact sub-class of ObjC TurboModule */ - [_performanceLogger getTurboModuleFromTMMDelegateStart:moduleName]; auto turboModule = [_delegate getTurboModule:moduleName initParams:params]; - [_performanceLogger getTurboModuleFromTMMDelegateEnd:moduleName]; if (turboModule != nullptr) { _turboModuleCache.insert({moduleName, turboModule}); } @@ -332,10 +338,21 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name moduleHolder = &_turboModuleHolders[moduleName]; } - return [self _provideRCTTurboModule:moduleName moduleHolder:moduleHolder]; + TurboModulePerfLogger::moduleCreateStart(moduleName, moduleHolder->getModuleId()); + id module = [self _provideRCTTurboModule:moduleName moduleHolder:moduleHolder shouldPerfLog:YES]; + + if (module) { + TurboModulePerfLogger::moduleCreateEnd(moduleName, moduleHolder->getModuleId()); + } else { + TurboModulePerfLogger::moduleCreateFail(moduleName, moduleHolder->getModuleId()); + } + + return module; } -- (id)_provideRCTTurboModule:(const char *)moduleName moduleHolder:(TurboModuleHolder *)moduleHolder +- (id)_provideRCTTurboModule:(const char *)moduleName + moduleHolder:(TurboModuleHolder *)moduleHolder + shouldPerfLog:(BOOL)shouldPerfLog { bool shouldCreateModule = false; @@ -343,6 +360,9 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name std::lock_guard guard(moduleHolder->mutex()); if (moduleHolder->isDoneCreatingModule()) { + if (shouldPerfLog) { + TurboModulePerfLogger::moduleCreateCacheHit(moduleName, moduleHolder->getModuleId()); + } return moduleHolder->getModule(); } @@ -358,7 +378,6 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name /** * Step 2a: Resolve platform-specific class. */ - [_performanceLogger getRCTTurboModuleClassStart:moduleName]; if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) { std::lock_guard delegateGuard(_turboModuleManagerDelegateMutex); @@ -370,13 +389,13 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name moduleClass = getFallbackClassFromName(moduleName); } - [_performanceLogger getRCTTurboModuleClassEnd:moduleName]; - __block id module = nil; if ([moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) { dispatch_block_t work = ^{ - module = [self _createAndSetUpRCTTurboModule:moduleClass moduleName:moduleName]; + module = [self _createAndSetUpRCTTurboModule:moduleClass + moduleName:moduleName + moduleId:moduleHolder->getModuleId()]; }; if ([self _requiresMainQueueSetup:moduleClass]) { @@ -422,15 +441,17 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name * - The main thread (if the TurboModule requires main queue init), blocking the thread that calls * provideRCTTurboModule:. */ -- (id)_createAndSetUpRCTTurboModule:(Class)moduleClass moduleName:(const char *)moduleName +- (id)_createAndSetUpRCTTurboModule:(Class)moduleClass + moduleName:(const char *)moduleName + moduleId:(int32_t)moduleId { id module = nil; /** * Step 2b: Ask hosting application/delegate to instantiate this class */ - [_performanceLogger getRCTTurboModuleInstanceStart:moduleName]; + TurboModulePerfLogger::moduleCreateConstructStart(moduleName, moduleId); if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) { std::lock_guard delegateGuard(_turboModuleManagerDelegateMutex); @@ -438,10 +459,9 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name } else { module = [moduleClass new]; } + TurboModulePerfLogger::moduleCreateConstructEnd(moduleName, moduleId); - [_performanceLogger getRCTTurboModuleInstanceEnd:moduleName]; - - [_performanceLogger setupRCTTurboModuleStart:moduleName]; + TurboModulePerfLogger::moduleCreateSetUpStart(moduleName, moduleId); if ([module respondsToSelector:@selector(setTurboModuleLookupDelegate:)]) { [module setTurboModuleLookupDelegate:self]; @@ -457,8 +477,6 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name * NativeModule. */ if ([module respondsToSelector:@selector(bridge)] && _bridge) { - [_performanceLogger attachRCTBridgeToRCTTurboModuleStart:moduleName]; - /** * Just because a NativeModule has the `bridge` method, it doesn't mean * that it has synthesized the bridge in its implementation. Therefore, @@ -483,8 +501,6 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name "or provide your own setter method.", RCTBridgeModuleNameForClass([module class])); } - - [_performanceLogger attachRCTBridgeToRCTTurboModuleEnd:moduleName]; } /** @@ -493,8 +509,6 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name * `@synthesize methodQueue = _methodQueue` */ - [_performanceLogger attachMethodQueueToRCTTurboModuleStart:moduleName]; - dispatch_queue_t methodQueue = nil; BOOL moduleHasMethodQueueGetter = [module respondsToSelector:@selector(methodQueue)]; @@ -541,8 +555,6 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name */ objc_setAssociatedObject(module, &kAssociatedMethodQueueKey, methodQueue, OBJC_ASSOCIATION_RETAIN); - [_performanceLogger attachMethodQueueToRCTTurboModuleEnd:moduleName]; - /** * NativeModules that implement the RCTFrameUpdateObserver protocol * require registration with RCTDisplayLink. @@ -551,10 +563,8 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name * rollout. */ if (_bridge) { - [_performanceLogger registerRCTTurboModuleForFrameUpdatesStart:moduleName]; RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:(id)module bridge:_bridge]; [_bridge registerModuleForFrameUpdates:(id)module withModuleData:data]; - [_performanceLogger registerRCTTurboModuleForFrameUpdatesEnd:moduleName]; } /** @@ -563,14 +573,12 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name * TODO(T41180176): Investigate whether we can delete this after TM * rollout. */ - [_performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:moduleName]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTDidInitializeModuleNotification object:_bridge userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil([_bridge parentBridge])}]; - [_performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:moduleName]; - [_performanceLogger setupRCTTurboModuleEnd:moduleName]; + TurboModulePerfLogger::moduleCreateSetUpEnd(moduleName, moduleId); return module; } @@ -643,40 +651,42 @@ - (void)installJSBindingWithRuntime:(jsi::Runtime *)runtime __weak __typeof(self) weakSelf = self; - react::TurboModuleBinding::install( - *_runtime, - [weakSelf, - performanceLogger = _performanceLogger](const std::string &name) -> std::shared_ptr { - if (!weakSelf) { - return nullptr; - } + TurboModuleBinding::install(*_runtime, [weakSelf](const std::string &name) -> std::shared_ptr { + if (!weakSelf) { + return nullptr; + } - __strong __typeof(self) strongSelf = weakSelf; + auto moduleName = name.c_str(); - auto moduleName = name.c_str(); - auto moduleWasNotInitialized = ![strongSelf moduleIsInitialized:moduleName]; - if (moduleWasNotInitialized) { - [strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup]; - } + TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName); - [performanceLogger createTurboModuleStart:moduleName]; + __strong __typeof(self) strongSelf = weakSelf; - /** - * By default, all TurboModules are long-lived. - * Additionally, if a TurboModule with the name `name` isn't found, then we - * trigger an assertion failure. - */ - auto turboModule = [strongSelf provideTurboModule:moduleName]; + auto moduleWasNotInitialized = ![strongSelf moduleIsInitialized:moduleName]; + if (moduleWasNotInitialized) { + [strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup]; + } - [performanceLogger createTurboModuleEnd:moduleName]; + /** + * By default, all TurboModules are long-lived. + * Additionally, if a TurboModule with the name `name` isn't found, then we + * trigger an assertion failure. + */ + auto turboModule = [strongSelf provideTurboModule:moduleName]; - if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) { - [strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup]; - [strongSelf notifyAboutTurboModuleSetup:moduleName]; - } + if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) { + [strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup]; + [strongSelf notifyAboutTurboModuleSetup:moduleName]; + } + + if (turboModule) { + TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName); + } else { + TurboModulePerfLogger::moduleJSRequireEndingFail(moduleName); + } - return turboModule; - }); + return turboModule; + }); } #pragma mark RCTTurboModuleLookupDelegate @@ -737,7 +747,9 @@ - (void)bridgeDidInvalidateModules:(NSNotification *)notification * for TurboModule init to finish before calling invalidate on it. So, we call _provideRCTTurboModule:moduleHolder, * because it's guaranteed to return a fully initialized NativeModule. */ - id module = [self _provideRCTTurboModule:moduleName.c_str() moduleHolder:moduleHolder]; + id module = [self _provideRCTTurboModule:moduleName.c_str() + moduleHolder:moduleHolder + shouldPerfLog:NO]; if ([module respondsToSelector:@selector(invalidate)]) { if ([module respondsToSelector:@selector(methodQueue)]) { diff --git a/ReactCommon/turbomodule/samples/BUCK b/ReactCommon/turbomodule/samples/BUCK index 44173730827899..ae15f4706b606f 100644 --- a/ReactCommon/turbomodule/samples/BUCK +++ b/ReactCommon/turbomodule/samples/BUCK @@ -26,7 +26,6 @@ rn_xplat_cxx_library( ], prefix = "ReactCommon", ), - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbandroid_srcs = glob( [ "platform/android/**/*.cpp", @@ -37,7 +36,6 @@ rn_xplat_cxx_library( "-fobjc-arc-exceptions", ], fbobjc_inherited_buck_flags = get_static_library_ios_flags(), - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], fbobjc_preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode(), force_static = True, ios_deps = [ @@ -63,6 +61,7 @@ rn_xplat_cxx_library( "platform/ios/**/*.mm", ], ), + labels = ["supermodule:xplat/default/public.react_native.infra"], platforms = (ANDROID, APPLE), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/ReactCommon/utils/BUCK b/ReactCommon/utils/BUCK index c9660e082d7157..9b525a1f1e037a 100644 --- a/ReactCommon/utils/BUCK +++ b/ReactCommon/utils/BUCK @@ -39,12 +39,11 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], - fbandroid_labels = ["supermodule:android/default/public.react_native.infra"], fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_frameworks = ["Foundation"], - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], 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 = [ diff --git a/ReactCommon/yoga/yoga/Utils.cpp b/ReactCommon/yoga/yoga/Utils.cpp index 761f3515ed6cae..f6e55d0d8fcc5c 100644 --- a/ReactCommon/yoga/yoga/Utils.cpp +++ b/ReactCommon/yoga/yoga/Utils.cpp @@ -65,3 +65,7 @@ YGFloatOptional YGFloatOptionalMax(YGFloatOptional op1, YGFloatOptional op2) { } return op1.isUndefined() ? op2 : op1; } + +void throwLogicalErrorWithMessage(const char* message) { + throw std::logic_error(message); +} diff --git a/ReactCommon/yoga/yoga/Utils.h b/ReactCommon/yoga/yoga/Utils.h index bce8dfcab329c9..e9edf2f96268f2 100644 --- a/ReactCommon/yoga/yoga/Utils.h +++ b/ReactCommon/yoga/yoga/Utils.h @@ -141,3 +141,5 @@ inline YGFloatOptional YGResolveValueMargin( const float ownerSize) { return value.isAuto() ? YGFloatOptional{0} : YGResolveValue(value, ownerSize); } + +void throwLogicalErrorWithMessage(const char* message); diff --git a/ReactCommon/yoga/yoga/Yoga.cpp b/ReactCommon/yoga/yoga/Yoga.cpp index c2a5c286f99580..91e09c15dac2bc 100644 --- a/ReactCommon/yoga/yoga/Yoga.cpp +++ b/ReactCommon/yoga/yoga/Yoga.cpp @@ -249,9 +249,6 @@ YOGA_EXPORT YGNodeRef YGNodeClone(YGNodeRef oldNode) { static YGConfigRef YGConfigClone(const YGConfig& oldConfig) { const YGConfigRef config = new YGConfig(oldConfig); YGAssert(config != nullptr, "Could not allocate memory for config"); - if (config == nullptr) { - abort(); - } gConfigInstanceCount++; return config; } @@ -4341,6 +4338,7 @@ YOGA_EXPORT void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour( void YGAssert(const bool condition, const char* message) { if (!condition) { Log::log(YGNodeRef{nullptr}, YGLogLevelFatal, nullptr, "%s\n", message); + throwLogicalErrorWithMessage(message); } } @@ -4350,6 +4348,7 @@ void YGAssertWithNode( const char* message) { if (!condition) { Log::log(node, YGLogLevelFatal, nullptr, "%s\n", message); + throwLogicalErrorWithMessage(message); } } @@ -4359,6 +4358,7 @@ void YGAssertWithConfig( const char* message) { if (!condition) { Log::log(config, YGLogLevelFatal, nullptr, "%s\n", message); + throwLogicalErrorWithMessage(message); } } diff --git a/ReactCommon/yoga/yoga/log.cpp b/ReactCommon/yoga/yoga/log.cpp index fe6fbbc6dc944a..eb3da039c3d071 100644 --- a/ReactCommon/yoga/yoga/log.cpp +++ b/ReactCommon/yoga/yoga/log.cpp @@ -26,10 +26,6 @@ void vlog( va_list args) { YGConfig* logConfig = config != nullptr ? config : YGConfigGetDefault(); logConfig->log(logConfig, node, level, context, format, args); - - if (level == YGLogLevelFatal) { - abort(); - } } } // namespace diff --git a/android-patches/patches/Focus/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/android-patches/patches/Focus/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index 1fbe78695d223b..3900a74d85bf05 100644 --- a/android-patches/patches/Focus/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/android-patches/patches/Focus/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -1,6 +1,16 @@ ---- "E:\\github\\rnm-63-fresh\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\views\\view\\ReactViewManager.java" 2020-10-27 20:26:16.993188900 -0700 -+++ "E:\\github\\rnm-63\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\views\\view\\ReactViewManager.java" 2020-10-13 21:46:27.236639400 -0700 -@@ -48,8 +48,13 @@ +diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +index 851ec10c6..4fe93de49 100644 +--- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java ++++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +@@ -25,6 +25,7 @@ import com.facebook.react.uimanager.PointerEvents; + import com.facebook.react.uimanager.Spacing; + import com.facebook.react.uimanager.ThemedReactContext; + import com.facebook.react.uimanager.UIManagerHelper; ++import com.facebook.react.uimanager.UIManagerModule; + import com.facebook.react.uimanager.ViewProps; + import com.facebook.react.uimanager.annotations.ReactProp; + import com.facebook.react.uimanager.annotations.ReactPropGroup; +@@ -48,8 +49,13 @@ public class ReactViewManager extends ReactClippingViewManager { Spacing.START, Spacing.END, }; @@ -16,7 +26,7 @@ private static final String HOTSPOT_UPDATE_KEY = "hotspotUpdate"; @ReactProp(name = "accessible") -@@ -120,6 +125,36 @@ +@@ -120,6 +126,36 @@ public class ReactViewManager extends ReactClippingViewManager { } } @@ -53,7 +63,7 @@ @ReactProp(name = "borderStyle") public void setBorderStyle(ReactViewGroup view, @Nullable String borderStyle) { view.setBorderStyle(borderStyle); -@@ -289,7 +324,7 @@ +@@ -287,7 +323,7 @@ public class ReactViewManager extends ReactClippingViewManager { @Override public Map getCommandsMap() { @@ -62,7 +72,7 @@ public Map getCommandsMap() { } @Override -@@ -305,6 +340,16 @@ +@@ -303,6 +339,16 @@ public class ReactViewManager extends ReactClippingViewManager { handleSetPressed(root, args); break; } @@ -79,7 +89,7 @@ public Map getCommandsMap() { } } -@@ -321,6 +366,16 @@ +@@ -319,6 +365,16 @@ public class ReactViewManager extends ReactClippingViewManager { handleSetPressed(root, args); break; } diff --git a/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 4bcd4b7693eb9a..82112a2052a785 100644 --- a/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -1,6 +1,8 @@ ---- "E:\\github\\rnm-63-fresh\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\ReactInstanceManager.java" 2020-10-27 20:26:16.728167300 -0700 -+++ "E:\\github\\rnm-63\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\ReactInstanceManager.java" 2020-10-13 21:26:17.779198100 -0700 -@@ -51,6 +51,7 @@ +diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +index 63ac2ec11..26fee8860 100644 +--- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java ++++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +@@ -51,6 +51,7 @@ import com.facebook.infer.annotation.ThreadConfined; import com.facebook.infer.annotation.ThreadSafe; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.CatalystInstance; @@ -8,7 +10,7 @@ import com.facebook.react.bridge.CatalystInstanceImpl; import com.facebook.react.bridge.JSBundleLoader; import com.facebook.react.bridge.JSIModule; -@@ -173,6 +174,7 @@ +@@ -173,6 +174,7 @@ public class ReactInstanceManager { private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; private final @Nullable JSIModulePackage mJSIModulePackage; private List mViewManagers; @@ -16,7 +18,7 @@ private class ReactContextInitParams { private final JavaScriptExecutorFactory mJsExecutorFactory; -@@ -922,6 +924,15 @@ +@@ -193,6 +195,15 @@ public class ReactInstanceManager { } } @@ -29,16 +31,16 @@ private class ReactContextInitParams { + mCatalystInstanceEventListener = catalystInstanceEventListener; + } + - /** Add a listener to be notified of react instance events. */ - public void addReactInstanceEventListener(ReactInstanceEventListener listener) { - mReactInstanceEventListeners.add(listener); -@@ -1245,7 +1256,8 @@ + /** Creates a builder that is capable of creating an instance of {@link ReactInstanceManager}. */ + public static ReactInstanceManagerBuilder builder() { + return new ReactInstanceManagerBuilder(); +@@ -1266,7 +1277,8 @@ public class ReactInstanceManager { .setJSExecutor(jsExecutor) .setRegistry(nativeModuleRegistry) .setJSBundleLoader(jsBundleLoader) - .setNativeModuleCallExceptionHandler(exceptionHandler); + .setNativeModuleCallExceptionHandler(exceptionHandler) -+ .setCatalystInstanceEventListener(mCatalystInstanceEventListener); ++ .setCatalystInstanceEventListener(mCatalystInstanceEventListener); ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START); // CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp diff --git a/android-patches/patches/V8/ReactAndroid/src/main/jni/react/jni/Android.mk b/android-patches/patches/V8/ReactAndroid/src/main/jni/react/jni/Android.mk index 89ac240b7673eb..1141aa30bb0fe6 100644 --- a/android-patches/patches/V8/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/android-patches/patches/V8/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -1,16 +1,17 @@ ---- "E:\\github\\rnm-63-fresh\\ReactAndroid\\src\\main\\jni\\react\\jni\\Android.mk" 2020-10-27 20:26:17.023172300 -0700 -+++ "E:\\github\\rnm-63\\ReactAndroid\\src\\main\\jni\\react\\jni\\Android.mk" 2020-10-13 21:47:10.404176500 -0700 -@@ -71,6 +71,7 @@ - $(call import-module,callinvoker) +diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk +index 38a51019e..7425e65a5 100644 +--- a/ReactAndroid/src/main/jni/react/jni/Android.mk ++++ b/ReactAndroid/src/main/jni/react/jni/Android.mk +@@ -72,6 +72,7 @@ $(call import-module,callinvoker) + $(call import-module,reactperflogger) $(call import-module,hermes) $(call import-module,runtimeexecutor) +$(call import-module,v8jsi) include $(REACT_SRC_DIR)/turbomodule/core/jni/Android.mk -@@ -82,3 +83,4 @@ +@@ -83,3 +84,4 @@ include $(REACT_SRC_DIR)/jscexecutor/Android.mk include $(REACT_SRC_DIR)/../hermes/reactexecutor/Android.mk include $(REACT_SRC_DIR)/../hermes/instrumentation/Android.mk include $(REACT_SRC_DIR)/modules/blob/jni/Android.mk +include $(REACT_SRC_DIR)/v8executor/Android.mk -\ No newline at end of file diff --git a/jest/renderer.js b/jest/renderer.js index e9f0cde6c4ad10..144b53c26292b7 100644 --- a/jest/renderer.js +++ b/jest/renderer.js @@ -15,8 +15,8 @@ const React = require('react'); const ShallowRenderer = require('react-test-renderer/shallow'); const TestRenderer = require('react-test-renderer'); -/* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses an error - * found when Flow v0.122.0 was deployed. To see the error, delete this comment +/* $FlowFixMe(>=0.125.1 site=react_native_fb) This comment suppresses an error + * found when Flow v0.125.1 was deployed. To see the error, delete this comment * and run Flow. */ const renderer = new ShallowRenderer(); diff --git a/package.json b/package.json index 96b394ccb04919..e6042c7e61cc92 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "pretty-format": "^26.0.1", "promise": "^8.0.3", "prop-types": "^15.7.2", + "qs": "^6.5.1", "react-devtools-core": "^4.6.0", "react-refresh": "^0.4.0", "regenerator-runtime": "^0.13.2", @@ -143,7 +144,7 @@ "eslint-plugin-react-hooks": "^3.0.0", "eslint-plugin-react-native": "3.8.1", "eslint-plugin-relay": "1.7.1", - "flow-bin": "^0.124.0", + "flow-bin": "^0.125.1", "flow-remove-types": "1.2.3", "hermes-engine-darwin": "~0.5.0", "jest": "^26.0.1", diff --git a/packages/eslint-config-react-native-community/README.md b/packages/eslint-config-react-native-community/README.md index d0dd6a118d3cf3..367ab70030ded3 100644 --- a/packages/eslint-config-react-native-community/README.md +++ b/packages/eslint-config-react-native-community/README.md @@ -5,7 +5,7 @@ ## Installation ``` -yarn add --dev eslint @react-native-community/eslint-config +yarn add --dev eslint prettier @react-native-community/eslint-config ``` *Note: We're using `yarn` to install deps. Feel free to change commands to use `npm` 3+ and `npx` if you like* diff --git a/packages/react-native-codegen/DEFS.bzl b/packages/react-native-codegen/DEFS.bzl index f54615a0494941..ce5d7225cd5bec 100644 --- a/packages/react-native-codegen/DEFS.bzl +++ b/packages/react-native-codegen/DEFS.bzl @@ -219,7 +219,6 @@ def rn_codegen_components( ], fbobjc_compiler_flags = get_apple_compiler_flags(), fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), - fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"], platforms = (ANDROID, APPLE, CXX), preprocessor_flags = [ "-DLOG_TAG=\"ReactNative\"", diff --git a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js index 08c99a48791373..dd0258a488ab7d 100644 --- a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js +++ b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js @@ -55,7 +55,8 @@ const propSetterTemplate = ` `; const commandsTemplate = ` - public void receiveCommand(::_INTERFACE_CLASSNAME_:: viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { ::_COMMAND_CASES_:: } @@ -198,7 +199,7 @@ function generateCommandCasesString( const commandMethods = component.commands .map(command => { return `case "${command.name}": - viewManager.${toSafeJavaString( + mViewManager.${toSafeJavaString( command.name, false, )}(${getCommandArguments(command)}); diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap index 914f8e56da71a9..fda2e0e535ed63 100644 --- a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap @@ -214,13 +214,14 @@ public class CommandNativeComponentManagerDelegate viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case \\"flashScrollIndicators\\": - viewManager.flashScrollIndicators(view); + mViewManager.flashScrollIndicators(view); break; case \\"allTypes\\": - viewManager.allTypes(view, args.getInt(0), (float) args.getDouble(1), args.getDouble(2), args.getString(3), args.getBoolean(4)); + mViewManager.allTypes(view, args.getInt(0), (float) args.getDouble(1), args.getDouble(2), args.getString(3), args.getBoolean(4)); break; } } @@ -264,13 +265,14 @@ public class CommandNativeComponentManagerDelegate viewManager, T view, String commandName, ReadableArray args) { + @Override + public void receiveCommand(T view, String commandName, ReadableArray args) { switch (commandName) { case \\"handleRootTag\\": - viewManager.handleRootTag(view, args.getDouble(0)); + mViewManager.handleRootTag(view, args.getDouble(0)); break; case \\"hotspotUpdate\\": - viewManager.hotspotUpdate(view, args.getInt(0), args.getInt(1)); + mViewManager.hotspotUpdate(view, args.getInt(0), args.getInt(1)); break; } } diff --git a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js b/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js index 416e7c549fb03d..f6ff56e544cdca 100644 --- a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js +++ b/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js @@ -51,12 +51,62 @@ function getSafePropertyName(name: string) { return name; } +function getNamespacedStructName(structName: string, propertyName: string) { + return `JS::Native::_MODULE_NAME_::::Spec${structName}${capitalizeFirstLetter( + getSafePropertyName(propertyName), + )}`; +} + +function getElementTypeForArray( + property: ObjectParamTypeAnnotation, + name: string, +): string { + const {typeAnnotation} = property; + if (typeAnnotation.type !== 'ArrayTypeAnnotation') { + throw new Error( + `Cannot get array element type for non-array type ${typeAnnotation.type}`, + ); + } + + if (!typeAnnotation.elementType) { + return 'id'; + } + + const {type} = typeAnnotation.elementType; + switch (type) { + case 'StringTypeAnnotation': + return 'NSString *'; + case 'DoubleTypeAnnotation': + case 'NumberTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + return 'double'; + case 'ObjectTypeAnnotation': + return getNamespacedStructName(name, property.name); + case 'GenericObjectTypeAnnotation': + // TODO T67565166: Generic objects are not type safe and should be disallowed in the schema. + return 'id'; + case 'BooleanTypeAnnotation': + case 'AnyObjectTypeAnnotation': + case 'AnyTypeAnnotation': + case 'ArrayTypeAnnotation': + case 'FunctionTypeAnnotation': + case 'ReservedFunctionValueTypeAnnotation': + case 'ReservedPropTypeAnnotation': + case 'StringEnumTypeAnnotation': + throw new Error(`Unsupported array element type, found: ${type}"`); + default: + (type: empty); + throw new Error(`Unknown array element type, found: ${type}"`); + } +} + function getInlineMethodSignature( property: ObjectParamTypeAnnotation, name: string, ): string { const {typeAnnotation} = property; - function markOptionalTypeIfNecessary(type) { + function markOptionalTypeIfNecessary(type: string) { if (property.optional) { return `folly::Optional<${type}>`; } @@ -86,22 +136,20 @@ function getInlineMethodSignature( case 'ObjectTypeAnnotation': return ( markOptionalTypeIfNecessary( - `JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter( - getSafePropertyName(property.name), - )}`, + getNamespacedStructName(name, property.name), ) + ` ${getSafePropertyName(property.name)}() const;` ); case 'GenericObjectTypeAnnotation': case 'AnyTypeAnnotation': - if (property.optional) { - return `id _Nullable ${getSafePropertyName( - property.name, - )}() const;`; - } - return `id ${getSafePropertyName(property.name)}() const;`; + return `id ${ + property.optional ? '_Nullable ' : ' ' + }${getSafePropertyName(property.name)}() const;`; case 'ArrayTypeAnnotation': return `${markOptionalTypeIfNecessary( - 'facebook::react::LazyVector>', + `facebook::react::LazyVector<${getElementTypeForArray( + property, + name, + )}>`, )} ${getSafePropertyName(property.name)}() const;`; case 'FunctionTypeAnnotation': default: @@ -114,18 +162,58 @@ function getInlineMethodImplementation( name: string, ): string { const {typeAnnotation} = property; - function markOptionalTypeIfNecessary(type) { + function markOptionalTypeIfNecessary(type: string): string { if (property.optional) { return `folly::Optional<${type}> `; } return `${type} `; } - function markOptionalValueIfNecessary(value) { + function markOptionalValueIfNecessary(value: string): string { if (property.optional) { return `RCTBridgingToOptional${capitalizeFirstLetter(value)}`; } return `RCTBridgingTo${capitalizeFirstLetter(value)}`; } + function bridgeArrayElementValueIfNecessary(element: string): string { + if (typeAnnotation.type !== 'ArrayTypeAnnotation') { + throw new Error( + `Cannot get array element type for non-array type ${typeAnnotation.type}`, + ); + } + + if (!typeAnnotation.elementType) { + throw new Error(`Cannot get array element type for ${name}`); + } + + const {type} = typeAnnotation.elementType; + switch (type) { + case 'StringTypeAnnotation': + return `RCTBridgingToString(${element})`; + case 'DoubleTypeAnnotation': + case 'NumberTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + return `RCTBridgingToDouble(${element})`; + case 'BooleanTypeAnnotation': + return `RCTBridgingToBool(${element})`; + case 'ObjectTypeAnnotation': + return `${getNamespacedStructName(name, property.name)}(${element})`; + case 'GenericObjectTypeAnnotation': + return element; + case 'AnyObjectTypeAnnotation': + case 'AnyTypeAnnotation': + case 'ArrayTypeAnnotation': + case 'FunctionTypeAnnotation': + case 'ReservedFunctionValueTypeAnnotation': + case 'ReservedPropTypeAnnotation': + case 'StringEnumTypeAnnotation': + case 'TupleTypeAnnotation': + throw new Error(`Unsupported array element type, found: ${type}"`); + default: + (type: empty); + throw new Error(`Unknown array element type, found: ${type}"`); + } + } switch (typeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': @@ -171,34 +259,37 @@ function getInlineMethodImplementation( .replace( /::_RETURN_TYPE_::/, markOptionalTypeIfNecessary( - `JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter( - getSafePropertyName(property.name), - )}`, + getNamespacedStructName(name, property.name), ), ) .replace( /::_RETURN_VALUE_::/, property.optional - ? `(p == nil ? folly::none : folly::make_optional(JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter( - getSafePropertyName(property.name), + ? `(p == nil ? folly::none : folly::make_optional(${getNamespacedStructName( + name, + property.name, )}(p)))` - : `JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter( - getSafePropertyName(property.name), - )}(p)`, + : `${getNamespacedStructName(name, property.name)}(p)`, ); case 'ArrayTypeAnnotation': return inlineTemplate .replace( /::_RETURN_TYPE_::/, markOptionalTypeIfNecessary( - 'facebook::react::LazyVector>', + `facebook::react::LazyVector<${getElementTypeForArray( + property, + name, + )}>`, ), ) .replace( /::_RETURN_VALUE_::/, - `${markOptionalValueIfNecessary( - 'vec', - )}(p, ^id(id itemValue_0) { return itemValue_0; })`, + `${markOptionalValueIfNecessary('vec')}(p, ^${getElementTypeForArray( + property, + name, + )}(id itemValue_0) { return ${bridgeArrayElementValueIfNecessary( + 'itemValue_0', + )}; })`, ); case 'FunctionTypeAnnotation': default: diff --git a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js index fd534a9af516fb..110476acc5f7f1 100644 --- a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js @@ -609,6 +609,87 @@ const COMPLEX_OBJECTS: SchemaType = { optional: true, }, }, + { + name: 'getArrays', + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + nullable: false, + type: 'VoidTypeAnnotation', + }, + params: [ + { + nullable: false, + name: 'options', + typeAnnotation: { + type: 'ObjectTypeAnnotation', + properties: [ + { + optional: false, + name: 'arrayOfNumbers', + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'NumberTypeAnnotation', + }, + }, + }, + { + optional: true, + name: 'optionalArrayOfNumbers', + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'NumberTypeAnnotation', + }, + }, + }, + { + optional: false, + name: 'arrayOfStrings', + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'StringTypeAnnotation', + }, + }, + }, + { + optional: true, + name: 'optionalArrayOfStrings', + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'StringTypeAnnotation', + }, + }, + }, + { + optional: false, + name: 'arrayOfObjects', + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'ObjectTypeAnnotation', + properties: [ + { + optional: false, + name: 'numberProperty', + typeAnnotation: { + type: 'NumberTypeAnnotation', + }, + }, + ], + }, + }, + }, + ], + }, + }, + ], + optional: false, + }, + }, ], }, }, diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap index e3b257ba994b24..be5e11a7f90a0a 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap @@ -29,11 +29,17 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_optionalMetho return jsi::Value::undefined(); } +static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getArrays(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + static_cast(&turboModule)->getArrays(rt, args[0].getObject(rt)); + return jsi::Value::undefined(); +} + NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule(\\"SampleTurboModule\\", jsInvoker) { methodMap_[\\"difficult\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_difficult}; methodMap_[\\"optionals\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_optionals}; methodMap_[\\"optionalMethod\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_optionalMethod}; + methodMap_[\\"getArrays\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getArrays}; } diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap index 308af3d47cdc32..8a4271cb0206f6 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap @@ -25,6 +25,7 @@ public: virtual jsi::Object difficult(jsi::Runtime &rt, const jsi::Object &A) = 0; virtual void optionals(jsi::Runtime &rt, const jsi::Object &A) = 0; virtual void optionalMethod(jsi::Runtime &rt, const jsi::Object &options, const jsi::Function &callback, const jsi::Array &extras) = 0; +virtual void getArrays(jsi::Runtime &rt, const jsi::Object &options) = 0; }; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap index 6005fc72f622f0..83c96819594ef0 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap @@ -72,11 +72,32 @@ namespace JS { @end +namespace JS { + namespace NativeSampleTurboModule { + struct SpecGetArraysOptions { + facebook::react::LazyVector arrayOfNumbers() const; + folly::Optional> optionalArrayOfNumbers() const; + facebook::react::LazyVector arrayOfStrings() const; + folly::Optional> optionalArrayOfStrings() const; + facebook::react::LazyVector arrayOfObjects() const; + + SpecGetArraysOptions(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptions) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptions:(id)json; +@end + + namespace JS { namespace NativeSampleTurboModule { struct SpecOptionalsA { folly::Optional optionalNumberProperty() const; - folly::Optional>> optionalArrayProperty() const; + folly::Optional> optionalArrayProperty() const; folly::Optional optionalObjectProperty() const; id _Nullable optionalGenericObjectProperty() const; folly::Optional optionalBooleanTypeProperty() const; @@ -179,10 +200,10 @@ inline folly::Optional JS::NativeSampleTurboModule::SpecOptionalsA::opti } -inline folly::Optional>> JS::NativeSampleTurboModule::SpecOptionalsA::optionalArrayProperty() const +inline folly::Optional> JS::NativeSampleTurboModule::SpecOptionalsA::optionalArrayProperty() const { id const p = _v[@\\"optionalArrayProperty\\"]; - return RCTBridgingToOptionalVec(p, ^id(id itemValue_0) { return itemValue_0; }); + return RCTBridgingToOptionalVec(p, ^double(id itemValue_0) { return RCTBridgingToDouble(itemValue_0); }); } @@ -207,6 +228,41 @@ inline folly::Optional JS::NativeSampleTurboModule::SpecOptionalsA::option } +inline facebook::react::LazyVector JS::NativeSampleTurboModule::SpecGetArraysOptions::arrayOfNumbers() const +{ + id const p = _v[@\\"arrayOfNumbers\\"]; + return RCTBridgingToVec(p, ^double(id itemValue_0) { return RCTBridgingToDouble(itemValue_0); }); +} + + +inline folly::Optional> JS::NativeSampleTurboModule::SpecGetArraysOptions::optionalArrayOfNumbers() const +{ + id const p = _v[@\\"optionalArrayOfNumbers\\"]; + return RCTBridgingToOptionalVec(p, ^double(id itemValue_0) { return RCTBridgingToDouble(itemValue_0); }); +} + + +inline facebook::react::LazyVector JS::NativeSampleTurboModule::SpecGetArraysOptions::arrayOfStrings() const +{ + id const p = _v[@\\"arrayOfStrings\\"]; + return RCTBridgingToVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); }); +} + + +inline folly::Optional> JS::NativeSampleTurboModule::SpecGetArraysOptions::optionalArrayOfStrings() const +{ + id const p = _v[@\\"optionalArrayOfStrings\\"]; + return RCTBridgingToOptionalVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); }); +} + + +inline facebook::react::LazyVector JS::NativeSampleTurboModule::SpecGetArraysOptions::arrayOfObjects() const +{ + id const p = _v[@\\"arrayOfObjects\\"]; + return RCTBridgingToVec(p, ^JS::NativeSampleTurboModule::SpecGetArraysOptionsArrayOfObjects(id itemValue_0) { return JS::NativeSampleTurboModule::SpecGetArraysOptionsArrayOfObjects(itemValue_0); }); +} + + inline bool JS::NativeSampleTurboModule::SpecDifficultAE::D() const { id const p = _v[@\\"D\\"]; @@ -256,6 +312,7 @@ inline double JS::NativeSampleTurboModule::SpecOptionalsAOptionalObjectProperty: - (void) optionalMethod:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback extras:(NSArray * _Nullable)extras; +- (void) getArrays:(JS::NativeSampleTurboModule::SpecGetArraysOptions&)options; @end diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap index 86abf78460cec5..3a278d825a7ae2 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap @@ -40,6 +40,14 @@ Map { @end +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptions) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptions:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + + @implementation RCTCxxConvert (NativeSampleTurboModule_SpecDifficultAE) + (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultAE:(id)json { @@ -73,13 +81,20 @@ namespace facebook { .invokeObjCMethod(rt, VoidKind, \\"optionalMethod\\", @selector(optionalMethod:callback:extras:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getArrays(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"getArrays\\", @selector(getArrays:), args, count); + } + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { methodMap_[\\"difficult\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_difficult}; methodMap_[\\"optionals\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_optionals}; methodMap_[\\"optionalMethod\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_optionalMethod}; + methodMap_[\\"getArrays\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArrays}; setMethodArgConversionSelector(@\\"difficult\\", 0, @\\"JS_NativeSampleTurboModule_SpecDifficultA:\\"); setMethodArgConversionSelector(@\\"optionals\\", 0, @\\"JS_NativeSampleTurboModule_SpecOptionalsA:\\"); + setMethodArgConversionSelector(@\\"getArrays\\", 0, @\\"JS_NativeSampleTurboModule_SpecGetArraysOptions:\\"); } } // namespace react } // namespace facebook diff --git a/packages/react-native-codegen/src/parsers/flow/modules/methods.js b/packages/react-native-codegen/src/parsers/flow/modules/methods.js index 7edb91ec1513e4..e6e34b6af2480b 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/methods.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/methods.js @@ -142,6 +142,7 @@ function getElementTypeForArrayOrObject( case 'UnionTypeAnnotation': return undefined; default: + // TODO T67565166: Generic objects are not type safe and should be disallowed in the schema. return { type: 'GenericObjectTypeAnnotation', }; diff --git a/scripts/generate-native-modules-specs-cli.js b/scripts/generate-native-modules-specs-cli.js new file mode 100644 index 00000000000000..2b72610e21b6c4 --- /dev/null +++ b/scripts/generate-native-modules-specs-cli.js @@ -0,0 +1,78 @@ +/** + * 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 RNCodegen = require('../packages/react-native-codegen/lib/generators/RNCodegen.js'); +const fs = require('fs'); +const mkdirp = require('mkdirp'); +const os = require('os'); +const path = require('path'); + +function generateSpec(schemaPath, outputDirectory) { + const libraryName = 'FBReactNativeSpec'; + const moduleSpecName = 'FBReactNativeSpec'; + const schemaText = fs.readFileSync(schemaPath, 'utf-8'); + + if (schemaText == null) { + throw new Error(`Can't find schema at ${schemaPath}`); + } + + const tempOutputDirectory = fs.mkdtempSync( + path.join(os.tmpdir(), 'react-native-codegen-'), + ); + + let schema; + try { + schema = JSON.parse(schemaText); + } catch (err) { + throw new Error(`Can't parse schema to JSON. ${schemaPath}`); + } + + RNCodegen.generate( + {libraryName, schema, outputDirectory: tempOutputDirectory, moduleSpecName}, + { + generators: [ + 'descriptors', + 'events', + 'props', + 'tests', + 'shadow-nodes', + 'modules', + ], + }, + ); + + if (!outputDirectory) { + outputDirectory = path.resolve( + __dirname, + '..', + 'Libraries', + libraryName, + moduleSpecName, + ); + } + mkdirp.sync(outputDirectory); + + const fileNames = [`${moduleSpecName}.h`, `${moduleSpecName}-generated.mm`]; + fileNames.forEach(fileName => { + const newOutput = `${tempOutputDirectory}/${fileName}`; + const prevOutput = `${outputDirectory}/${fileName}`; + fs.copyFileSync(newOutput, prevOutput); + }); +} + +function main() { + const args = process.argv.slice(2); + const schemaPath = args[0]; + const outputDir = args[1]; + generateSpec(schemaPath, outputDir); +} + +main(); diff --git a/scripts/generate-native-modules-specs.sh b/scripts/generate-native-modules-specs.sh index 505a7769a338b0..6f77c36c503603 100755 --- a/scripts/generate-native-modules-specs.sh +++ b/scripts/generate-native-modules-specs.sh @@ -27,26 +27,21 @@ describe () { THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd) RN_DIR=$(cd "$THIS_DIR/.." && pwd) -TEMP_DIR=$(mktemp -d /tmp/react-native-codegen-XXXXXXXX) - SRCS_DIR=$(cd "$RN_DIR/Libraries" && pwd) -LIBRARY_NAME="FBReactNativeSpec" -MODULE_SPEC_NAME="FBReactNativeSpec" - SCHEMA_FILE="$RN_DIR/schema-native-modules.json" -OUTPUT_DIR="$SRCS_DIR/$LIBRARY_NAME/$MODULE_SPEC_NAME" - -describe "Generating schema from flow types" -grep --exclude NativeUIManager.js --include=Native\*.js -rnwl "$SRCS_DIR" -e 'export interface Spec extends TurboModule' -e "export default \(TurboModuleRegistry.get(Enforcing)?\('.*\): Spec\);/" \ - | xargs yarn flow-node packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js "$SCHEMA_FILE" - -describe "Generating native code from schema" -yarn flow-node packages/react-native-codegen/buck_tests/generate-tests.js "$SCHEMA_FILE" "$LIBRARY_NAME" "$TEMP_DIR" "$MODULE_SPEC_NAME" if [ -n "$1" ]; then OUTPUT_DIR="$1" - mkdir -p "$OUTPUT_DIR" fi -describe "Copying $MODULE_SPEC_NAME output to $OUTPUT_DIR" -cp "$TEMP_DIR"/$MODULE_SPEC_NAME* "$OUTPUT_DIR/." +pushd "$RN_DIR/packages/react-native-codegen" >/dev/null + yarn + yarn build +popd >/dev/null + +describe "Generating schema from flow types" +grep --exclude NativeUIManager.js --include=Native\*.js -rnwl "$SRCS_DIR" -e 'export interface Spec extends TurboModule' -e "export default \(TurboModuleRegistry.get(Enforcing)?\('.*\): Spec\);/" \ + | xargs yarn node packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js "$SCHEMA_FILE" + +describe "Generating native code from schema" +yarn node scripts/generate-native-modules-specs-cli.js "$SCHEMA_FILE" "$OUTPUT_DIR" diff --git a/scripts/react_native_pods.rb b/scripts/react_native_pods.rb index 2cdade9df62ac7..6b1c0f4d084682 100644 --- a/scripts/react_native_pods.rb +++ b/scripts/react_native_pods.rb @@ -46,6 +46,7 @@ def use_react_native! (options={}) pod 'React-jsinspector', :path => "#{prefix}/ReactCommon/jsinspector" pod 'React-callinvoker', :path => "#{prefix}/ReactCommon/callinvoker" pod 'React-runtimeexecutor', :path => "#{prefix}/ReactCommon/runtimeexecutor" + pod 'React-perflogger', :path => "#{prefix}/ReactCommon/reactperflogger" pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon" pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true diff --git a/template.config.js b/template.config.js index 634e9e13c48bcd..e2516f3a4e8278 100644 --- a/template.config.js +++ b/template.config.js @@ -7,5 +7,6 @@ module.exports = { placeholderName: 'HelloWorld', + titlePlaceholder: 'Hello App Display Name', templateDir: './template', }; diff --git a/template/_flowconfig b/template/_flowconfig index 0296b285f8cf61..24b28ccc0e1c21 100644 --- a/template/_flowconfig +++ b/template/_flowconfig @@ -47,10 +47,6 @@ suppress_type=$FlowFixMe suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError - [lints] sketchy-null-number=warn sketchy-null-mixed=warn @@ -73,4 +69,4 @@ untyped-import untyped-type-import [version] -^0.124.0 +^0.125.0 diff --git a/template/ios/HelloWorld/Info.plist b/template/ios/HelloWorld/Info.plist index 20f7dd5114c9c5..948d58224f9473 100644 --- a/template/ios/HelloWorld/Info.plist +++ b/template/ios/HelloWorld/Info.plist @@ -26,8 +26,6 @@ NSAppTransportSecurity - NSAllowsArbitraryLoads - NSExceptionDomains localhost diff --git a/tools/build_defs/oss/rn_defs.bzl b/tools/build_defs/oss/rn_defs.bzl index 698ce671363feb..887e25d3651577 100644 --- a/tools/build_defs/oss/rn_defs.bzl +++ b/tools/build_defs/oss/rn_defs.bzl @@ -87,6 +87,9 @@ def react_native_integration_tests_target(path): def react_native_dep(path): return "//ReactAndroid/src/main/" + path +def react_native_android_toplevel_dep(path): + return react_native_dep(path) + # Example: react_native_xplat_dep('java/com/facebook/systrace:systrace') def react_native_xplat_dep(path): return "//ReactCommon/" + path @@ -183,12 +186,18 @@ def rn_robolectric_test(name, srcs, vm_args = None, *args, **kwargs): is_androidx = kwargs.pop("is_androidx", False) + kwargs["deps"] = kwargs.pop("deps", []) + [ + react_native_android_toplevel_dep("third-party/java/mockito2:mockito2"), + react_native_dep("libraries/fbcore/src/test/java/com/facebook/powermock:powermock2"), + react_native_dep("third-party/java/robolectric/4.3.1:robolectric"), + ] + extra_vm_args = [ "-XX:+UseConcMarkSweepGC", # required by -XX:+CMSClassUnloadingEnabled "-XX:+CMSClassUnloadingEnabled", "-XX:ReservedCodeCacheSize=150M", - "-Drobolectric.dependency.dir=buck-out/gen/ReactAndroid/src/main/third-party/java/robolectric3/robolectric", - "-Dlibraries=buck-out/gen/ReactAndroid/src/main/third-party/java/robolectric3/robolectric/*.jar", + "-Drobolectric.dependency.dir=buck-out/gen/ReactAndroid/src/main/third-party/java/robolectric/4.3.1", + "-Dlibraries=buck-out/gen/ReactAndroid/src/main/third-party/java/robolectric/4.3.1/*.jar", "-Drobolectric.logging.enabled=true", "-XX:MaxPermSize=620m", "-Drobolectric.offline=true", diff --git a/yarn.lock b/yarn.lock index 1489a15c9d62ee..4200c81b4d5580 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,18 +10,18 @@ "@babel/highlight" "^7.8.3" "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.4.5", "@babel/core@^7.7.5": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" - integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.0.tgz#a6fe5db77ebfb61e0da6c5c36aaf14aab07b2b44" + integrity sha512-FGgV2XyPoVtYDvbFXlukEWt13Afka4mBRQ2CoTsHxpgVGO6XfgtT6eI+WyjQRGGTL90IDkIVmme8riFCLZ8lUw== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" + "@babel/generator" "^7.10.0" "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.0" - "@babel/parser" "^7.9.0" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" + "@babel/helpers" "^7.10.0" + "@babel/parser" "^7.10.0" + "@babel/template" "^7.10.0" + "@babel/traverse" "^7.10.0" + "@babel/types" "^7.10.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" @@ -31,22 +31,12 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.5.0", "@babel/generator@^7.9.0": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.4.tgz#12441e90c3b3c4159cdecf312075bf1a8ce2dbce" - integrity sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA== - dependencies: - "@babel/types" "^7.9.0" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/generator@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" - integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== +"@babel/generator@^7.10.0", "@babel/generator@^7.5.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.0.tgz#a238837896edf35ee5fbfb074548d3256b4bc55d" + integrity sha512-ThoWCJHlgukbtCP79nAK4oLqZt5fVo70AHUni/y8Jotyg5rtJiG2FVl+iJjRNKIyl4hppqztLyAoEWcCvqyOFQ== dependencies: - "@babel/types" "^7.9.5" + "@babel/types" "^7.10.0" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" @@ -67,13 +57,13 @@ "@babel/types" "^7.8.3" "@babel/helper-builder-react-jsx-experimental@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz#066d80262ade488f9c1b1823ce5db88a4cedaa43" - integrity sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ== + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.5.tgz#0b4b3e04e6123f03b404ca4dfd6528fe6bb92fe3" + integrity sha512-HAagjAC93tk748jcXpZ7oYRZH485RCq/+yEv9SIWezHRPv9moZArTnkUNciUNzvwHUABmiWKlcxJvMcu59UwTg== dependencies: "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-module-imports" "^7.8.3" - "@babel/types" "^7.9.0" + "@babel/types" "^7.9.5" "@babel/helper-builder-react-jsx@^7.9.0": version "7.9.0" @@ -83,16 +73,16 @@ "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/types" "^7.9.0" -"@babel/helper-create-class-features-plugin@^7.8.3": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.6.tgz#243a5b46e2f8f0f674dc1387631eb6b28b851de0" - integrity sha512-klTBDdsr+VFFqaDHm5rR69OpEQtO2Qv8ECxHS1mNhJJvaHArR6a1xTf5K/eZW7eZpJbhCx3NW1Yt/sKsLXLblg== +"@babel/helper-create-class-features-plugin@^7.10.0", "@babel/helper-create-class-features-plugin@^7.8.3": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.0.tgz#3a2b7b86f6365ea4ac3837a49ec5791e65217944" + integrity sha512-n4tPJaI0iuLRayriXTQ8brP3fMA/fNmxpxswfNuhe4qXQbcCWzeAqm6SeR/KExIOcdCvOh/KkPQVgBsjcb0oqA== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-member-expression-to-functions" "^7.10.0" + "@babel/helper-optimise-call-expression" "^7.10.0" "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-replace-supers" "^7.10.0" "@babel/helper-split-export-declaration" "^7.8.3" "@babel/helper-create-regexp-features-plugin@^7.8.3": @@ -121,16 +111,7 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" - integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-function-name@^7.9.5": +"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== @@ -146,12 +127,12 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== +"@babel/helper-member-expression-to-functions@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.0.tgz#e8cf57470bfd1247f2b41aa621a527e952efa6f1" + integrity sha512-xKLTpbMkJcvwEsDaTfs9h0IlfUiBLPFfybxaPpPPsQDsZTRg+UKh+86oK7sctHF3OUiRQkb10oS9MXSqgyV6/g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.0" "@babel/helper-module-imports@^7.8.3": version "7.8.3" @@ -173,12 +154,12 @@ "@babel/types" "^7.9.0" lodash "^4.17.13" -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== +"@babel/helper-optimise-call-expression@^7.10.0", "@babel/helper-optimise-call-expression@^7.8.3": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.0.tgz#6dcfb565842f43bed31b24f3e4277f18826e5e79" + integrity sha512-HgMd8QKA8wMJs5uK/DYKdyzJAEuGt1zyDp9wLMlMR6LitTQTHPUE+msC82ZsEDwq+U3/yHcIXIngRm9MS4IcIg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.0" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.8.3" @@ -203,15 +184,15 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== +"@babel/helper-replace-supers@^7.10.0", "@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.0.tgz#26bc22ee1a35450934d2e2a9b27de10a22fac9d6" + integrity sha512-erl4iVeiANf14JszXP7b69bSrz3e3+qW09pVvEmTWwzRQEOoyb1WFlYCA8d/VjVZGYW8+nGpLh7swf9CifH5wg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/helper-member-expression-to-functions" "^7.10.0" + "@babel/helper-optimise-call-expression" "^7.10.0" + "@babel/traverse" "^7.10.0" + "@babel/types" "^7.10.0" "@babel/helper-simple-access@^7.8.3": version "7.8.3" @@ -228,12 +209,7 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" - integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== - -"@babel/helper-validator-identifier@^7.9.5": +"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== @@ -248,14 +224,14 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helpers@^7.9.0": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" - integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== +"@babel/helpers@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.0.tgz#634400a0977b8dcf7b311761a77ca94ed974b3b6" + integrity sha512-lQtFJoDZAGf/t2PgR6Z59Q2MwjvOGGsxZ0BAlsrgyDhKuMbe63EfbQmVmcLfyTBj8J4UtiadQimcotvYVg/kVQ== dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" + "@babel/template" "^7.10.0" + "@babel/traverse" "^7.10.0" + "@babel/types" "^7.10.0" "@babel/highlight@^7.8.3": version "7.9.0" @@ -266,10 +242,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" - integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.10.0", "@babel/parser@^7.7.5": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.0.tgz#8eca3e71a73dd562c5222376b08253436bb4995b" + integrity sha512-fnDUl1Uy2gThM4IFVW4ISNHqr3cJrCsRkSCasFgx0XDO9JcttDS5ytyBc4Cu4X1+fjoo3IVvFbRD6TeFlHJlEQ== "@babel/plugin-check-constants@^7.0.0-beta.38": version "7.0.0-beta.38" @@ -308,12 +284,13 @@ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz#a28993699fc13df165995362693962ba6b061d6f" - integrity sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.0.tgz#d27b0910b637f7c9d9a5629f2adcd04dc9ea4e69" + integrity sha512-DOD+4TqMcRKJdAfN08+v9cciK5d0HW5hwTndOoKZEfEzU/mRrKboheD5mnWU4Q96VOnDdAj86kKjZhoQyG6s+A== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.9.5" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.8.3" @@ -324,9 +301,9 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.0.tgz#f9bdcd5cbf2e3037674903a45e56ed0cbaea1550" + integrity sha512-bn+9XT8Y6FJCO37ewj4E1gIirR35nDm+mGcqQV4dM3LKSVp3QTAU3f65Z0ld4y6jdfAlv2VKzCh4mezhRnl+6Q== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.0" @@ -460,21 +437,21 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.0.tgz#5d7aa0cf921ec91bdc97c9b311bf1fce0ea979b0" + integrity sha512-AoMn0D3nLG9i71useuBrZZTnHbjnhcaTXCckUtOx3JPuhGGJdOUYMwOV9niPJ+nZCk52dfLLqbmV3pBMCRQLNw== dependencies: "@babel/helper-plugin-utils" "^7.8.3" lodash "^4.17.13" "@babel/plugin-transform-classes@^7.0.0": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz#8603fc3cc449e31fdbdbc257f67717536a11af8d" - integrity sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ== + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" + integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== dependencies: "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-define-map" "^7.8.3" - "@babel/helper-function-name" "^7.8.3" + "@babel/helper-function-name" "^7.9.5" "@babel/helper-optimise-call-expression" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-replace-supers" "^7.8.6" @@ -489,9 +466,9 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz#fadb2bc8e90ccaf5658de6f8d4d22ff6272a2f4b" - integrity sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.0.tgz#59145194029721e49e511afb4bdd1d2f38369180" + integrity sha512-yKoghHpYbC0eM+6o6arPUJT9BQBvOOn8iOCEHwFvkJ5gjAxYmoUaAuLwaoA9h2YvC6dzcRI0KPQOpRXr8qQTxQ== dependencies: "@babel/helper-plugin-utils" "^7.8.3" @@ -512,9 +489,9 @@ "@babel/plugin-syntax-flow" "^7.8.3" "@babel/plugin-transform-for-of@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.0.tgz#ff2bf95dc1deb9b309c7fd78d9620ac9266a3efe" + integrity sha512-0ldl5xEe9kbuhB1cDqs17JiBPEm1+6/LH7loo29+MAJOyB/xbpLI/u6mRzDPjr0nYL7z0S14FPT4hs2gH8Im9Q== dependencies: "@babel/helper-plugin-utils" "^7.8.3" @@ -540,17 +517,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940" - integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-simple-access" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-commonjs@^7.1.0": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz#64b7474a4279ee588cacd1906695ca721687c277" integrity sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ== @@ -575,10 +542,10 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-replace-supers" "^7.8.3" -"@babel/plugin-transform-parameters@^7.0.0": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz#3028d0cc20ddc733166c6e9c8534559cee09f54a" - integrity sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" + integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== dependencies: "@babel/helper-get-function-arity" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" @@ -606,9 +573,9 @@ "@babel/plugin-syntax-jsx" "^7.8.3" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz#89ef93025240dd5d17d3122294a093e5e0183de0" - integrity sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.0.tgz#0e24978505130a79bb8ee1af15a1a7d8e783347d" + integrity sha512-EmUZ2YYXK6YFIdSxUJ1thg7gIBMHSEp8nGS6GwkXGpGdplpmOhj6azYjszT8YcFt6HyPElycDOd2lXckzN+OEw== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" @@ -631,9 +598,9 @@ regenerator-transform "^0.14.2" "@babel/plugin-transform-runtime@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz#45468c0ae74cc13204e1d3b1f4ce6ee83258af0b" - integrity sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.0.tgz#16e50ba682aa9925b94123a622d996cadd4cbef7" + integrity sha512-SWIc5IJnoLHk9qVRvvpebUW5lafStcKlLcqELMiNOApVIxPbCtkQfLRMCdaEKw4X92JItFKdoBxv2udiyGwFtg== dependencies: "@babel/helper-module-imports" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" @@ -648,9 +615,9 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-spread@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.0.tgz#6918d9b2b52c604802bd50a5f22b649efddf9af6" + integrity sha512-P3Zj04ylqumJBjmjylNl05ZHRo4j4gFNG7P70loys0//q5BTe30E8xIj6PnqEWAfsPYu2sdIPcJeeQdclqlM6A== dependencies: "@babel/helper-plugin-utils" "^7.8.3" @@ -671,11 +638,11 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-typescript@^7.5.0", "@babel/plugin-transform-typescript@^7.9.0": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.4.tgz#4bb4dde4f10bbf2d787fce9707fb09b483e33359" - integrity sha512-yeWeUkKx2auDbSxRe8MusAG+n4m9BFY/v+lPjmQDgOFX5qnySkUY5oXzkp6FwPdsYqnKay6lorXYdC0n3bZO7w== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.0.tgz#00273cddb1f5321af09db5c096bb865eab137124" + integrity sha512-BGH4yn+QwYFfzh8ITmChwrcvhLf+jaYBlz+T87CNKTP49SbqrjqTsMqtFijivYWYjcYHvac8II53RYd82vRaAw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.0" "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-typescript" "^7.8.3" @@ -715,73 +682,40 @@ source-map-support "^0.5.16" "@babel/runtime@^7.0.0", "@babel/runtime@^7.8.4": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" - integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.0.tgz#2cdcd6d7a391c24f7154235134c830cfb58ac0b1" + integrity sha512-tgYb3zVApHbLHYOPWtVwg25sBqHhfBXRKeKoTIyoheIxln1nA7oBl7SfHfiTG2GhDPI8EUBkOD/0wJCP/3HN4Q== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.0.0", "@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/template@^7.0.0", "@babel/template@^7.10.0", "@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.0.tgz#f15d852ce16cd5fb3e219097a75f662710b249b1" + integrity sha512-aMLEQn5tcG49LEWrsEwxiRTdaJmvLem3+JMCMSeCy2TILau0IDVyWdm/18ACx7XOCady64FLt6KkHy28tkDQHQ== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/parser" "^7.10.0" + "@babel/types" "^7.10.0" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" - integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.10.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.0.tgz#290935529881baf619398d94fd453838bef36740" + integrity sha512-NZsFleMaLF1zX3NxbtXI/JCs2RPOdpGru6UBdGsfhdsDsP+kFF+h2QQJnMJglxk0kc69YmMFs4A44OJY0tKo5g== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.0" - "@babel/types" "^7.9.0" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/traverse@^7.7.4": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2" - integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.5" + "@babel/generator" "^7.10.0" "@babel/helper-function-name" "^7.9.5" "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.0" - "@babel/types" "^7.9.5" + "@babel/parser" "^7.10.0" + "@babel/types" "^7.10.0" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" - integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== - dependencies: - "@babel/helper-validator-identifier" "^7.9.0" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@babel/types@^7.3.3": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" - integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== - dependencies: - "@babel/helper-validator-identifier" "^7.9.5" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@babel/types@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" - integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== +"@babel/types@^7.0.0", "@babel/types@^7.10.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.8.3", "@babel/types@^7.9.0", "@babel/types@^7.9.5": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.0.tgz#d47d92249e42393a5723aad5319035ae411e3e38" + integrity sha512-t41W8yWFyQFPOAAvPvjyRhejcLGnJTA3iRpFcDbEKwVJ3UnHQePFzLk8GagTsucJlImyNwrGikGsYURrWbQG8w== dependencies: "@babel/helper-validator-identifier" "^7.9.5" lodash "^4.17.13" @@ -1743,13 +1677,6 @@ babel-jest@^26.0.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -3350,10 +3277,10 @@ flat-cache@^1.2.1: rimraf "~2.6.2" write "^0.2.1" -flow-bin@^0.124.0: - version "0.124.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.124.0.tgz#24b2e55874e1e2041f9247f42473b3db2ef32758" - integrity sha512-KEtDJ7CFUjcuhw6N52FTZshDd1krf1fxpp4APSIrwhVm+IrlcKJ+EMXpeXKM1kKNSZ347dYGh8wEvXQl4pHZEA== +flow-bin@^0.125.1: + version "0.125.1" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.125.1.tgz#7edbc71e7dc39ddef18086ef75c714bbf1c5917f" + integrity sha512-jEury9NTXylxQEOAXLWEE945BjBwYcMwwKVnb+5XORNwMQE7i5hQYF0ysYfsaaYOa7rW/U16rHBfwLuaZfWV7A== flow-parser@0.*: version "0.89.0" @@ -6248,6 +6175,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qs@^6.5.1: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"