Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -2354,12 +2354,14 @@ public class com/facebook/react/fabric/mounting/SurfaceMountingManager {
public fun sendAccessibilityEvent (II)V
public fun setJSResponder (IIZ)V
public fun stopSurface ()V
public fun storeSynchronousMountPropsOverride (ILcom/facebook/react/bridge/ReadableMap;)V
public fun sweepActiveTouchForTag (I)V
public fun updateEventEmitter (ILcom/facebook/react/fabric/events/EventEmitterWrapper;)V
public fun updateLayout (IIIIIIII)V
public fun updateOverflowInset (IIIII)V
public fun updatePadding (IIIII)V
public fun updateProps (ILcom/facebook/react/bridge/ReadableMap;)V
public fun updatePropsSynchronously (ILcom/facebook/react/bridge/ReadableMap;)V
public fun updateState (ILcom/facebook/react/uimanager/StateWrapper;)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,8 @@ public void synchronouslyUpdateViewOnUIThread(final int reactTag, final Readable
@Override
public void execute(MountingManager mountingManager) {
try {
mountingManager.updateProps(reactTag, props);
mountingManager.storeSynchronousMountPropsOverride(reactTag, props);
mountingManager.updatePropsSynchronously(reactTag, props);
} catch (Exception ex) {
// TODO T42943890: Fix animations in Fabric and remove this try/catch?
// There might always be race conditions between surface teardown and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,23 @@ internal class MountingManager(
}

@UiThread
fun updateProps(reactTag: Int, props: ReadableMap?) {
fun storeSynchronousMountPropsOverride(reactTag: Int, props: ReadableMap?) {
assertOnUiThread()
if (props == null) {
return
}

getSurfaceManagerForViewEnforced(reactTag).updateProps(reactTag, props)
getSurfaceManagerForViewEnforced(reactTag).storeSynchronousMountPropsOverride(reactTag, props)
}

@UiThread
fun updatePropsSynchronously(reactTag: Int, props: ReadableMap?) {
assertOnUiThread()
if (props == null) {
return
}

getSurfaceManagerForViewEnforced(reactTag).updatePropsSynchronously(reactTag, props)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@
import com.facebook.react.bridge.ReactSoftExceptionLogger;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.RetryableMountingLayerException;
import com.facebook.react.bridge.SoftAssertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.annotations.UnstableReactNativeAPI;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.common.mapbuffer.MapBuffer;
Expand All @@ -53,7 +57,10 @@
import com.facebook.react.uimanager.events.EventCategoryDef;
import com.facebook.systrace.Systrace;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
Expand Down Expand Up @@ -96,6 +103,11 @@ public class SurfaceMountingManager {
// This is null *until* StopSurface is called.
private SparseArrayCompat<Object> mTagSetForStoppedSurface;

// This is to make sure direct manipulation result will not be overridden by React update.
@ThreadConfined(UI)
private final SparseArrayCompat<Map<String, Object>> mTagToSynchronousMountProps =
new SparseArrayCompat<>();

private final int mSurfaceId;

public SurfaceMountingManager(
Expand Down Expand Up @@ -682,13 +694,110 @@ public void createViewUnsafe(
}
}

private static void overridePropsReadableMap(
Map<String, Object> patchMap, WritableMap outputReadableMap) {
for (Map.Entry<String, Object> entry : patchMap.entrySet()) {
String propKey = entry.getKey();
if (outputReadableMap.hasKey(propKey)) {
Object propValue = entry.getValue();
if (propKey.equals("transform")) {
assert (outputReadableMap.getType(propKey) == ReadableType.Array
&& propValue instanceof ArrayList);
WritableArray array = new WritableNativeArray();
for (Object item : (ArrayList<?>) propValue) {
if (item instanceof HashMap) {
WritableNativeMap itemMap = new WritableNativeMap();
for (Map.Entry<String, Object> itemEntry :
((HashMap<String, Object>) item).entrySet()) {
if (itemEntry.getValue() instanceof String) {
itemMap.putString(itemEntry.getKey(), (String) itemEntry.getValue());
} else if (itemEntry.getValue() instanceof Number) {
itemMap.putDouble(
itemEntry.getKey(), ((Number) itemEntry.getValue()).doubleValue());
}
}
array.pushMap(itemMap);
}
}
outputReadableMap.putArray(propKey, array);
} else if (propKey.equals("opacity")) {
assert (outputReadableMap.getType(propKey) == ReadableType.Number
&& propValue instanceof Number);
outputReadableMap.putDouble(propKey, ((Number) propValue).doubleValue());
}
}
}
}

private static Map<String, Object> getHashMapFromPropsReadableMap(ReadableMap readableMap) {
HashMap<String, Object> outputMap = new HashMap<>();

Iterator<Map.Entry<String, Object>> iter = readableMap.getEntryIterator();
while (iter.hasNext()) {
Map.Entry<String, Object> entry = iter.next();
String propKey = entry.getKey();
Object propValue = entry.getValue();
if (propKey.equals("transform") && propValue instanceof ReadableArray) {
ArrayList<HashMap<String, Object>> arrayList = new ArrayList<>();
for (int i = 0; i < ((ReadableArray) propValue).size(); i++) {
ReadableMap map = ((ReadableArray) propValue).getMap(i);
if (map != null) {
arrayList.add(map.toHashMap());
}
}
outputMap.put(propKey, arrayList);
} else if (propKey.equals("opacity") && propValue instanceof Number) {
outputMap.put(propKey, ((Number) propValue).doubleValue());
}
}

return outputMap;
}

public void storeSynchronousMountPropsOverride(int reactTag, ReadableMap props) {
if (ReactNativeFeatureFlags.overrideBySynchronousMountPropsAtMountingAndroid()) {
Map<String, Object> propsMap = getHashMapFromPropsReadableMap(props);
if (mTagToSynchronousMountProps.containsKey(reactTag)) {
Map<String, Object> mergedPropsMap =
Assertions.assertNotNull(mTagToSynchronousMountProps.get(reactTag));
mergedPropsMap.putAll(propsMap);
mTagToSynchronousMountProps.put(reactTag, mergedPropsMap);
} else {
mTagToSynchronousMountProps.put(reactTag, propsMap);
}
}
}

public void updatePropsSynchronously(int reactTag, ReadableMap props) {
updateProps(reactTag, props, true);
}

public void updateProps(int reactTag, ReadableMap props) {
updateProps(reactTag, props, false);
}

@UiThread
private void updateProps(
int reactTag, ReadableMap props, Boolean shouldSkipSynchronousMountPropsOverride) {
if (isStopped()) {
return;
}

ViewState viewState = getViewState(reactTag);
viewState.mCurrentProps = new ReactStylesDiffMap(props);

if (ReactNativeFeatureFlags.overrideBySynchronousMountPropsAtMountingAndroid()
&& !shouldSkipSynchronousMountPropsOverride
&& mTagToSynchronousMountProps.containsKey(reactTag)) {
WritableMap modifiedProps = new WritableNativeMap();
modifiedProps.merge(props);
Map<String, Object> directPropsMap =
Assertions.assertNotNull(mTagToSynchronousMountProps.get(reactTag));
overridePropsReadableMap(directPropsMap, modifiedProps);
viewState.mCurrentProps = new ReactStylesDiffMap(modifiedProps);
} else {
viewState.mCurrentProps = new ReactStylesDiffMap(props);
}

View view = viewState.mView;

if (view == null) {
Expand Down Expand Up @@ -1057,6 +1166,11 @@ public void deleteView(int reactTag) {
return;
}

if (ReactNativeFeatureFlags.overrideBySynchronousMountPropsAtMountingAndroid()
&& mTagToSynchronousMountProps.containsKey(reactTag)) {
mTagToSynchronousMountProps.remove(reactTag);
}

ViewState viewState = getNullableViewState(reactTag);

if (viewState == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<92a6edded037a504fc1c2c8ae88deae1>>
* @generated SignedSource<<c217318f9f313103ca31d8e7698851d8>>
*/

/**
Expand Down Expand Up @@ -324,6 +324,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun hideOffscreenVirtualViewsOnIOS(): Boolean = accessor.hideOffscreenVirtualViewsOnIOS()

/**
* Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated.
*/
@JvmStatic
public fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = accessor.overrideBySynchronousMountPropsAtMountingAndroid()

/**
* Enable the V2 in-app Performance Monitor. This flag is global and should not be changed across React Host lifetimes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ebbc6602fc8e3fb4c510bbd47361a7f0>>
* @generated SignedSource<<4ed350d8dfa42caf27d346dfcfbed974>>
*/

/**
Expand Down Expand Up @@ -69,6 +69,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var fuseboxEnabledReleaseCache: Boolean? = null
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null
private var perfMonitorV2EnabledCache: Boolean? = null
private var preparedTextCacheSizeCache: Double? = null
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
Expand Down Expand Up @@ -531,6 +532,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean {
var cached = overrideBySynchronousMountPropsAtMountingAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.overrideBySynchronousMountPropsAtMountingAndroid()
overrideBySynchronousMountPropsAtMountingAndroidCache = cached
}
return cached
}

override fun perfMonitorV2Enabled(): Boolean {
var cached = perfMonitorV2EnabledCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<4410628511f112f3eef22434a05fa757>>
* @generated SignedSource<<6c201de02071a834e411b6761d7f863b>>
*/

/**
Expand Down Expand Up @@ -126,6 +126,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun hideOffscreenVirtualViewsOnIOS(): Boolean

@DoNotStrip @JvmStatic public external fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean

@DoNotStrip @JvmStatic public external fun perfMonitorV2Enabled(): Boolean

@DoNotStrip @JvmStatic public external fun preparedTextCacheSize(): Double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c2662bb149153d80e1b42029f7377eb9>>
* @generated SignedSource<<88256ff0f51c33caee5af50bb3424288>>
*/

/**
Expand Down Expand Up @@ -121,6 +121,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun hideOffscreenVirtualViewsOnIOS(): Boolean = false

override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = false

override fun perfMonitorV2Enabled(): Boolean = false

override fun preparedTextCacheSize(): Double = 200.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2f65e0e9066a8c2c35c44ea7c73dbedc>>
* @generated SignedSource<<57fca2370d6b3f1edf7045a0b4c94350>>
*/

/**
Expand Down Expand Up @@ -73,6 +73,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var fuseboxEnabledReleaseCache: Boolean? = null
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null
private var perfMonitorV2EnabledCache: Boolean? = null
private var preparedTextCacheSizeCache: Double? = null
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
Expand Down Expand Up @@ -584,6 +585,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}

override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean {
var cached = overrideBySynchronousMountPropsAtMountingAndroidCache
if (cached == null) {
cached = currentProvider.overrideBySynchronousMountPropsAtMountingAndroid()
accessedFeatureFlags.add("overrideBySynchronousMountPropsAtMountingAndroid")
overrideBySynchronousMountPropsAtMountingAndroidCache = cached
}
return cached
}

override fun perfMonitorV2Enabled(): Boolean {
var cached = perfMonitorV2EnabledCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e97601660cfd4f614ac934ba20ca4635>>
* @generated SignedSource<<ef31518f0fe8ff6f6a5c15936cab9d87>>
*/

/**
Expand Down Expand Up @@ -121,6 +121,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun hideOffscreenVirtualViewsOnIOS(): Boolean

@DoNotStrip public fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean

@DoNotStrip public fun perfMonitorV2Enabled(): Boolean

@DoNotStrip public fun preparedTextCacheSize(): Double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f9c4f8d97edff1b016873b0a6121b76b>>
* @generated SignedSource<<846db2d7c3c6020dec04789fe2784f8a>>
*/

/**
Expand Down Expand Up @@ -333,6 +333,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}

bool overrideBySynchronousMountPropsAtMountingAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("overrideBySynchronousMountPropsAtMountingAndroid");
return method(javaProvider_);
}

bool perfMonitorV2Enabled() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("perfMonitorV2Enabled");
Expand Down Expand Up @@ -702,6 +708,11 @@ bool JReactNativeFeatureFlagsCxxInterop::hideOffscreenVirtualViewsOnIOS(
return ReactNativeFeatureFlags::hideOffscreenVirtualViewsOnIOS();
}

bool JReactNativeFeatureFlagsCxxInterop::overrideBySynchronousMountPropsAtMountingAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid();
}

bool JReactNativeFeatureFlagsCxxInterop::perfMonitorV2Enabled(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::perfMonitorV2Enabled();
Expand Down Expand Up @@ -980,6 +991,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"hideOffscreenVirtualViewsOnIOS",
JReactNativeFeatureFlagsCxxInterop::hideOffscreenVirtualViewsOnIOS),
makeNativeMethod(
"overrideBySynchronousMountPropsAtMountingAndroid",
JReactNativeFeatureFlagsCxxInterop::overrideBySynchronousMountPropsAtMountingAndroid),
makeNativeMethod(
"perfMonitorV2Enabled",
JReactNativeFeatureFlagsCxxInterop::perfMonitorV2Enabled),
Expand Down
Loading
Loading