Summary:
Follow up to https://github.com/facebook/react-native/issues/27803. To keep consistency between the implementations this also implements autoFocus natively on Android. This will allow removing the JS auto focus logic completely.
## Changelog
[Android] [Fixed] - Implement native TextInput autoFocus on Android
Pull Request resolved: https://github.com/facebook/react-native/pull/27924
Test Plan: Test using the TextInput example in RN tester.
Differential Revision: D19674506
Pulled By: TheSavior
fbshipit-source-id: 9cbb517fc69bccd11f5292a1ccb4acea2e3713e9
Summary:
This diff changes the behavior of UIImplementation.resolveViewManager() to return null instead of throwing an exception when trying to find an unexistent viewManager during the computation of constants for view Managers.
The C++/JS code manages exceptions and null results when a view manager doesn't exists, this diff simplifies the way this method operates.
changeLog: [internal]
Reviewed By: rickhanlonii
Differential Revision: D19624423
fbshipit-source-id: df31dcfae9a588bf325b61d529cec6ead59fb19d
Summary:
I hit a crash when testing bridgeless mode in a release build:
Error: Exception in HostFunction: java.lang.NoSuchMethodError: no non-static method "Lcom/facebook/react/modules/core/JavaTimerManager;.createTimer(IJZ)V"
It turns out that `JavaTimerManager.createTimer()` is getting stripped from release builds because it's not referenced in Java at all. Adding `DoNotStrip` annotation to keep it around. The other methods in JavaTimerManager don't need this because they're referenced by TimingModule - this is the only method that's only used directly from C++ by bridgeless mode.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D19655519
fbshipit-source-id: 8b9862475986bb84b12d81f73f677cc2e4860c67
Summary:
Previously, users of the TurboModuleManager system would have to `TurboModuleManager.installBindings()` themselves, which synchronously attached the `__turboModuleProxy` function on the JS `global` object.
After these changes, the TurboModuleManager, when created will schedule work on the JS thread to install the global `__turboModuleProxy` function. As long as you create the TurboModuleManager before you run the bundle, we'll install the bindings before any TurboModules are accessed.
Changelog:
[Android][Fixed] - Install TM Bindings on JS thread
Reviewed By: ejanzer
Differential Revision: D19335956
fbshipit-source-id: 967ac2d3a0510392f6f0e42efe79b1a0ff6768c4
Summary: Migrate ReactTextinputManager to support bridgeless mode by accessing the EventDispatcher through the UIManagerHelper instead of from ReactContext.
Reviewed By: mdvacca
Differential Revision: D19614184
fbshipit-source-id: 5dd4945223d10785f8fe171e06d6f7ef42f9d834
Summary:
## Description
You're not supposed to hold on to JSI objects (ex: `jsi::Function`) past the point where their `jsi::Runtime` is deleted. Otherwise, we get a dangling pointer crash, like this: T60262810! Historically, this cleanup problem has always been really tricky to get right. With this diff, I hope to fix that problem once and for all by deleting all `jsi::Function`s when we delete the global `__turboModuleProxy` function.
## Current Setup
- The TurboModules infra uses weak references to `CallbackWrapper`s to hold on to the `jsi::Function`s passed from JS to ObjC.
- The LongLivedObjectCollection holds on to strong references to `CallbackWrapper`s. This ensures that the `jsi::Function`s aren't deleted prematurely. This also means that we can use `LongLivedObjectCollection` to delete all `CallbackWrappers`.
- `TurboModuleBinding` is the abstraction we use to install the global `__turboModuleProxy` function. It is owned by `TurboModuleManager`, and `TurboModuleManager` uses it to clear all references to `jsi::Function`s, when we delete all NativeModules.
## Solution
1. Transfer ownership of `TurboModuleBinding` from `TurboModuleManager` to the `__turboModuleProxy` function.
2. Clear the `LongLivedObjectCollection` when `TurboModuleBinding` is deleted.
Changelog:
[iOS][Fixed] - Clear all held jsi::Functions when jsi::Runtime is deleted
Reviewed By: JoshuaGross
Differential Revision: D19565499
fbshipit-source-id: e3510ea04e72f6bda363a8fc3ee2be60303b70a6
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/27841
Follow up from https://github.com/facebook/react-native/pull/27539 - adding back in the optimization that Detox has in TimersIdlingResource to avoid iterating over the entire timers queue if the next timer is already within the specified range.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D19522346
fbshipit-source-id: 32609f434d1ca575a5a49ad630e288c43fa90864
Summary:
Migrate ReactTextinputManager to support bridgeless mode by accessing the EventDispatcher through the UIManagerHelper instead of from ReactContext.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D19456326
fbshipit-source-id: 5e9643793cd764e29964f565db24cdea683ac032
Summary:
Migrating ReactImageView to use the new API for accessing the EventDispatcher (UIManagerHelper.getEventDispatcherForReactTag) that supports bridgeless mode.
Changelog: [Internal]
Reviewed By: PeteTheHeat
Differential Revision: D19190765
fbshipit-source-id: a958dfc9c48845270e99f8d378ce034cddc8036f
Summary:
Collecting Telemetry is a crucial part of building a performant UI framework; we do that but we need to improve it to make the data more reliable, actionable and trustful.
Now we collect time points as the number of milliseconds from the start of the CLOCK_MONOTONIC epoch. That's fine but it also has problems:
Sometimes a millisecond is an eternity. We have only 16 (or fewer) of them on each frame. What if some operation takes 1ms (according to telemetry) but we have to run it a dozen times? Does it mean that it's 12 ms in total? So, we lack precision.
This is not type-safe. Do you know how many milliseconds in a microsecond? I don't. We multiply that on magical constants hoping that we copied that from some other place right.
The current implementation is not cross-platform. We have ifdefs for iOS and Android and Unix and Windows (which is now implemented).
So, this diff replaces that with using `std::chrono` which is part of the standard library that designed to fix all those concerns. We also define our type-aliases on top of that to express our concrete constrains:
We use `std::chrono::steady_clock` as the base clock which is according to the standard using `clock_gettime(CLOCK_MONOTONIC, ... )` if available. So, it's fast and compatible (the same under the hood) with Android infra.
We use nanoseconds when we store time durations (TelemetryDuration type).
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: JoshuaGross, mdvacca
Differential Revision: D19184569
fbshipit-source-id: 7a44688f4bb3bfc6e3009874f0075c531c8569a1
Summary:
Based on [this](https://stackoverflow.com/a/5259004), header names are not case sensitive.
That means that it's valid to pass a header `Origin` or `origin`.
With the current implementation. on Android only, if you pass `Origin`, it will get overwritten by the default origin.
This made me waste a lot of time debugging a problem while trying to connect to a websockets server that required an `origin` header and my implementation was working fine in iOS but not on Android, so I'm suggest changing the logic a little bit to take that into account.
## Changelog
[Android] [Fixed] - Support for case insensitive "Origin" headers for Websockets
Pull Request resolved: https://github.com/facebook/react-native/pull/27827
Test Plan:
Here's a screenshot of that shows the issue before the fix (`Origin` header set, but getting overridden)

The fix is not that easy to test since it requires a public websocket server that checks for a custom Origin header, but I think the code changes are very small and clear.
Differential Revision: D19578860
Pulled By: cpojer
fbshipit-source-id: d854e887d1b9e8e54da662b2da2ebe08ce65fdbc
Summary:
It seems like DisplayMetricsHolder doesn't have any thread safety policy, but in practice it only updates DisplayMetrics from the main thread. Let's formalize that by adding some annotations to that effect.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D19417331
fbshipit-source-id: f1d464f228776197fd0df2978c9e8edbaab67850
Summary:
We should have been calling this already. Trivial fix. The intent of the view command was always to update selection, I just forgot to add it. See test videos.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D19548178
fbshipit-source-id: 14a1bdc11d84c05f2435c48c3bb96b64e8c68cb4
Summary:
Since migrating to Turbomodules (8fe04cf) the addMenuItem method crashes because the NativeEventListener methods are missing from the codegen flow type. Added the same methods based on what we do in AppState which is another native module that extends NativeEventListener.
## Changelog
[Internal] [Fixed] - Add missing NativeEventListener methods to NativeDevSettings
Pull Request resolved: https://github.com/facebook/react-native/pull/27838
Test Plan:
|Before|After|
|{F226978596}|{F226978628}
Differential Revision: D19518474
Pulled By: PeteTheHeat
fbshipit-source-id: acddba9f18dd558df1d6df78b539689fdfd0062f
Summary:
Changelog: [Internal]
The inspector API doesn't really need a `HermesRuntime`, all it needs is a `jsi::Runtime` and a `Debugger &`.
Change the return type of `RuntimeAdapter::getRuntime` to be `jsi::Runtime`.
This will allow the inspector to use the tracing runtime instead of the direct hermes runtime.
Reviewed By: willholen
Differential Revision: D18973867
fbshipit-source-id: 6809e52452a35e62be9ca8143aeaba8964c98eaa
Summary:
This diff refactors the UIManagerHelper.getUIManager to allow the caller determine if it should return null when catalyst Istance is not active.
This is necessary in order to keep backward compatibility for the getEventDispatcher method.
changelog: [internal]
Reviewed By: makovkastar
Differential Revision: D19383063
fbshipit-source-id: 8a46b61d212480be91ea78929bbfa7248d5f3ad9
Summary:
A previous PR broke toggling between visible and non-visible password (basically once a password field was made visible, future updates to the keyboardType were effectively ignored, so the password would always be visible).
Original PR: https://github.com/facebook/react-native/pull/27523
Changelog: [Internal]
Reviewed By: mdvacca, rodrigos-facebook
Differential Revision: D19527245
fbshipit-source-id: a5ab343c8a0c6a608171dbfa5afc7536ff241826
Summary:
This diff refactors the usages of UIManagerHelper.getUIManager() to make sure we always consider null objects.
Some of the callsites were throwing a NullPointerExcetpion, now they throw a more explicit exception.
changelog: [internal]
Reviewed By: makovkastar
Differential Revision: D19383064
fbshipit-source-id: 1806a37528e80cab1c8fdff5eb631aaf47bde819
Summary: On iOS we don't emit the didUpdateDimensions event in JS when the first React Native screen is rendered, so let's keep the behavior the same on Android.
Reviewed By: mdvacca
Differential Revision: D19506829
fbshipit-source-id: d0122d18be79177318c3f059ed396f990eeabcb7
Summary:
This is a trivial cleanup of an unused variable.
changelog: [internal]
Reviewed By: makovkastar
Differential Revision: D19383062
fbshipit-source-id: 937c8bb6de3aeebefb07940809340104654619fc
Summary:
This is an easy refactor of constants in ImageResizeMode
changelog: [Internal]
Reviewed By: JoshuaGross
Differential Revision: D19509348
fbshipit-source-id: 2bed9e35f0c7daa04c64ec7fba6975517633bfa7
Summary:
This PR makes it possible for subclasses of `ReactTextInputShadowNode` to control the construction of the "dummy" `EditText` instance that `ReactTextInputShadowNode` internally uses to determine the expected height of the view. This PR does not change the default behavior, it just opens up that default to being overriden.
This is useful in the case of custom views that have different behavior from a "default" `EditText` instance (`new EditText(context)`). For example, it might have a different style applied.
As a side benefit, this change also makes it easy to have subclasses not apply the default theme, which can allow the custom view to avoid a longstanding crash issue (https://github.com/facebook/react-native/issues/17530).
## Changelog
[Android] [Added] - Allow overriding `EditText` construction in `ReactTextInputShadowNode`
Pull Request resolved: https://github.com/facebook/react-native/pull/27782
Test Plan: All tests pass.
Reviewed By: mdvacca
Differential Revision: D19450593
Pulled By: JoshuaGross
fbshipit-source-id: 8d2ce6117246fc3e2108623312b38583af5722b3
Summary:
This is an incomplete effort to migrate from libfb to libfbjni. This is needed to restore the compatibility with Flipper and other FB Android projects that make use of FBJNI. Effectively, the outcome is that `fbjni` no longer has a checked-in copy here, but instead relies on the public artifacts published at github.com/facebookincubator/fbjni that can be deduplicated at build-time.
**A non-exhaustive list of tasks:**
* [X] Gradle builds the SDK and RNTester for Android.
* [X] Buck build for rntester works in OSS.
* [ ] Move from `java-only` release to full `fbjni` release. This requires finding a solution for stripping out `.so` files that the old `Android.mk` insists on including in the final artifacts and will clash with the full distribution.
* [ ] Import this and fix potential internal build issues.
* [ ] Verify that the changes made to the Hermes integration don't have any unintended consequences.
## Changelog
[Android] [Changed] - Migrated from libfb to libfbjni for JNI calls
Pull Request resolved: https://github.com/facebook/react-native/pull/27729
Test Plan:
- CI is already passing again for Gradle and Buck in OSS.
- After applying the following patch, RNTester builds and works with the latest Flipper SDK:
```
diff --git a/RNTester/android/app/build.gradle b/RNTester/android/app/build.gradle
index b8a6437d7..eac942104 100644
--- a/RNTester/android/app/build.gradle
+++ b/RNTester/android/app/build.gradle
@@ -170,10 +170,19 @@ dependencies {
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
- debugImplementation("com.facebook.flipper:flipper:0.23.4") {
+ debugImplementation("com.facebook.flipper🐬+") {
exclude group:'com.facebook.yoga'
- exclude group:'com.facebook.flipper', module: 'fbjni'
- exclude group:'com.facebook.litho', module: 'litho-annotations'
+ exclude group:'com.facebook.fbjni'
+ }
+
+ debugImplementation("com.facebook.flipper:flipper-network-plugin:+") {
+ exclude group:'com.facebook.yoga'
+ exclude group:'com.facebook.fbjni'
+ }
+
+ debugImplementation("com.facebook.flipper:flipper-fresco-plugin:+") {
+ exclude group:'com.facebook.yoga'
+ exclude group:'com.facebook.fbjni'
}
if (useIntlJsc) {
```
Reviewed By: mdvacca
Differential Revision: D19345270
Pulled By: passy
fbshipit-source-id: 33811e7f97f44f2ec5999e1c35339909dc4fd3b1
Summary:
LocalData isn't used by any components. We've moved towards State.
Changelog: [internal]
Reviewed By: mdvacca
Differential Revision: D19250737
fbshipit-source-id: 10bf0b62ffad01ad10b07d029e84df4f312780a1
Summary:
This diff ensures the measurement and rendering of FB emojis is correnct in RN Android.
Before this commit we were customizing Spannable object with FB emojis right before rendering the text into the TextView, this diff ensures that the Spannable is "customized" as soon as it is created, ensuring the measurement of the Text.
changelog: [internal]
Reviewed By: JoshuaGross
Differential Revision: D19354107
fbshipit-source-id: 92e07cf30503404f7820f25eaa9efdc02f6bddbd
Summary:
After some more testing, I discovered a problem in D19395326 because the native map that DeviceInfoModule was storing in `mPreviousDisplayMetrics` had been consumed when the event was emitted to JS. This caused the comparison to fail, so it would emit the event again when the dimensions hadn't changed.
In this diff, I'm storing a Java-only copy of the native map before emitting the event to JS so this shouldn't happen.
Changelog: [Android][Fixed] Fix bug in updating dimensions in JS
Reviewed By: mdvacca
Differential Revision: D19462861
fbshipit-source-id: 2e47479df93377b85fe87f255972dd31e874e3a8
Summary:
ReactRootView currently caches the last seen DisplayMetrics so we can compare them against the current values in DisplayMetricsHolder to determine if the screen dimensions have changed. (If they have changed, we emit an event to notify JS of the new dimensions).
However, ReactRootView initializes these member variables with empty DisplayMetrics, which means that the first time we check against them in onGlobalLayout, we will *always* emit an event to JS.
This seems reasonable if you only have one ReactRootView, but if you create a new RRV for each RN screen in your app, then you're going to get updated dimensions on each navigation event, even though the screen dimensions have probably not changed.
In this diff, I'm no longer storing the DisplayMetrics in ReactRootView at all, but instead am using temporary variables to check the new DisplayMetrics values against the old.
Changelog: [Android][Fixed] Only update dimensions in ReactRootView if they've changed
Reviewed By: JoshuaGross, mdvacca
Differential Revision: D19395326
fbshipit-source-id: c01aee73064764503c9b49208032c790b83a1d29
Summary:
For some reason, I think this method is being optimized away, which is really strange considering that it's being used in getModule.
Changelog:
[Internal]
Reviewed By: ejanzer
Differential Revision: D19454855
fbshipit-source-id: 414b4888f7aacf730dd22939e2e2140b94dff4e7
Summary:
We should also call `TurboModuleManager.getLegacyCxxModule('foo')` when we call `TurboModuleManager.getModule('foo')` is called.
This fixes a Marketplace crash. See: D19432594
Changelog:
[Android][Fixed] - Ensure TMM.getModule also queries CxxModuleWrappers
Reviewed By: ejanzer
Differential Revision: D19434549
fbshipit-source-id: cff741cf1587d2a0dbcdc5eb95016c8aa283b727
Summary:
This converts all NativeModules excluding the following into TurboModules:
```
// Deleted
"fbsource/fbandroid/java/com/facebook/catalyst/modules/relaynativecache/RelayNativeCacheReaderModule.java"
// Owners of FBC will migrate these by themselves
"fbsource/xplat/fbc-mobile-app/android/app/src/main/java/com/fbc/i18n/FBCi18nAssetsModule.java"
"fbsource/xplat/fbc-mobile-app/android/app/src/main/java/com/fbc/react/CellScanResultsModule.java"
"fbsource/xplat/fbc-mobile-app/android/app/src/main/java/com/fbc/react/WiFiScanResultsModule.java"
// Don't have Buck owners
"fbsource/xplat/js/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBAccessTokenModule.java"
"fbsource/xplat/js/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBGraphRequestModule.java"
"fbsource/xplat/js/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBLoginManagerModule.java"
"fbsource/xplat/js/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBShareAPIModule.java"
"fbsource/xplat/intl/oss-fbt/__github__/react-native-fbt-android-native-module/android/src/main/java/com/reactlibrary/FbtAndroidNativeModule.java"
```
This should conclude the Android TurboModule migration.
Changelog:
[Android][Added] - Make remaining NativeModules TurboModule-compatible
Reviewed By: PeteTheHeat
Differential Revision: D19383442
fbshipit-source-id: 71beaee087f6436b197a65f0d68527d9964bb6ce
Summary:
For every untyped NativeModule Java file, we ensure that its owner depends on the owner of its JS spec.
Changelog:
[Internal]
Reviewed By: PeteTheHeat
Differential Revision: D19382937
fbshipit-source-id: 0a1d840bff2f9e8db0f06c910448e9b25415d18c
Summary:
This diff fixes the redbox error: Views nested within a <Text> must have a width and height
This error is reproducible when rendering a View with no fixed size, inside a <Text>. e.g.
```
function PlaygroundContent(props: {}) {
return (
<View style={styles.container}>
<Text>
<View>
<Image source={fbicon.filled('chevron-down', 10)} />
</View>
</Text>
</View>
);
}
```
changelog: Add support to render <View> with no fixed size nested within a <Text>
Reviewed By: shergin
Differential Revision: D19387760
fbshipit-source-id: a9cee8410e56a2d362d6b8f993e602719c416925
Summary:
In D15393464, we introduced `NativeHeapCapture`, but it had a few problems:
1. It required `JSCHeapCapture` by doing `TurboModuleRegistry.get<Spec>('HeapCapture')`, when it should have done `TurboModuleRegistry.get<Spec>('JSCHeapCapture')`.
2. It had an additional method `captureHeap`, which didn't exist on the Android NativeModule.
This diff corrects those mistakes.
Changelog:
[Both][Fixed] - Fix JSCHeapCapture
Reviewed By: PeteTheHeat
Differential Revision: D19383511
fbshipit-source-id: 30e69afbcdba673f3f32c16bde4f0342568ab97d
Summary:
Ran `arc f` against some CPP files to be modified with the next diff
to reduce the churn on it.
## Changelog
[Android] [Changed] - Formatted cpp/h code with clang-format
Reviewed By: javache
Differential Revision: D19371785
fbshipit-source-id: b7f7b92c4cb9ec7f8da728bb577db29cf11fbb39
Summary:
In D14244606 I "fixed" `Platform.isTesting()` in JS. By fixed, I made it return true when running SSTs.
People in OSS complained about this in discord and [github](https://github.com/facebook/react-native/issues/27010). The problem is that this call returns true whenever an RN project references Detox in the build.gradle file. In practice, this has been really annoying, because it has disabled animations in debug builds, due to D13811035.
The fix is to be more specific, and look for the exact screenshot test activity. I haven't explicitly verified this doesn't trigger from Detox, but it shouldn't. I'll coordinate on the github issue to verify.
Changelog: [Android][Fixed] Fix animations in OSS debug builds by modifying `Platform.isTesting()` behaviour
Reviewed By: TheSavior
Differential Revision: D19384098
fbshipit-source-id: 22c885219f2c00f5dcc3b930b068bfd2ad7e4b8e
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/27539
Detox currently relies on reflection to inspect the private timers queue in the Timing module to check if React Native is idle or not. This broke when I renamed TimingModule and moved the queue to JavaTimerManager in D17260848 and D17282187. A better solution to this problem is for us to expose a public API for checking the timers queue from TimingModule, so that Detox doesn't need to access private fields.
Using similar logic to Detox's TimersIdlingResource: https://github.com/wix/Detox/blob/4f81a77baee4e4542a693922bcbde621d9d8c1a5/detox/android/detox/src/main/java/com/wix/detox/reactnative/idlingresources/TimersIdlingResource.kt#L95
Changelog: [Android] [Added] Added an API for checking if there are busy timers to TimingModule
Reviewed By: makovkastar
Differential Revision: D19128786
fbshipit-source-id: 835ae214eba58879c8343255bba680a81801ce03
Summary:
Fix for https://github.com/facebook/react-native/issues/27510.
Setting the `InputType.TYPE_CLASS_TEXT` flag when `keyboardType` is null or default breaks autoCapitalize. Handle the case when `keyboardType` is null, default, or invalid type.
## Changelog
[Android] [Fixed] - Fix setting keyboardType from breaking autoCapitalize
Pull Request resolved: https://github.com/facebook/react-native/pull/27523
Test Plan:
Added keyboardType prop to RNTester as so
```
<TextInput autoCapitalize="words" keyboardType="default" style={styles.default} />
```

Reviewed By: makovkastar
Differential Revision: D19132261
Pulled By: JoshuaGross
fbshipit-source-id: be66f0317ed305425ebcff32046ad4bff06d367f
Summary:
NativeAnimatedModule registers itself as a listener on UIManagerModule, which doesn't exist in bridgeless mode. We now have an API on ReactContext to detect if we're in bridgeless mode, so let's just bail out when that's the case (for now).
In the future, we'll need a replacement for this API on FabricUIManager (or somewhere).
Changelog: [Internal]
Reviewed By: PeteTheHeat, mdvacca
Differential Revision: D19185762
fbshipit-source-id: 1cf53304ab58a5b985c8f4806544da51f09e8ba5
Summary:
In bridgeless mode, we use BridgelessReactContext, which overrides some methods on ReactApplicationContext like `getJSIModule` and returns true for `isBridgeless`. This is needed for things like getting the EventDispatcher, which is currently accessed from the UIManagerModule (which doesn't exist in bridgeless mode).
However, when we create Views in React Native we don't use the ReactApplicationContext directly; instead, we create a ThemedReactContext, which holds a reference to the RAC. It also initializes itself with the RAC's CatalystInstance, so that when you call methods on the TRC it can access native modules, etc.
This doesn't work in bridgeless mode, because the methods are overridden on the RAC, *not* the TRC. So in order for this work as expected, we need to delegate these methods to the RAC member variable. In this diff I'm just doing this for `isBridgeless` and `getJSIModule` so that accessing the EventDispatcher works.
Changelog: [Internal]
Reviewed By: makovkastar
Differential Revision: D19190760
fbshipit-source-id: 6dc38560edc1061aec782707306590fa1012d5cb