Remove Fabric android implementation

Summary: This diff removes the Fabric Android implementation in favor of Fabric C++, as part of another diff I'm going to move the fabricxx package into fabric package

Reviewed By: shergin

Differential Revision: D9841240

fbshipit-source-id: c7922b7bfb9885f33b1f52237ec7cf00c1df96fb
This commit is contained in:
David Vacca 2018-09-19 07:59:07 -07:00 committed by Facebook Github Bot
parent 5c23f20cee
commit 7e7040b7bd
37 changed files with 28 additions and 2947 deletions

View File

@ -13,6 +13,8 @@ rn_android_library(
"PUBLIC",
],
deps = [
react_native_dep("java/com/facebook/fbreact/fabricxx:fabricxx"),
react_native_dep("java/com/facebook/fbreact/fabricxx/jsi:jsi"),
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/android/support/v4:lib-support-v4"),
react_native_dep("third-party/java/buck-android-support:buck-android-support"),
@ -28,7 +30,6 @@ rn_android_library(
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/devsupport:interfaces"),
react_native_target("java/com/facebook/react/fabric:fabric"),
react_native_target("java/com/facebook/react/fabric/jsc:jsc"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/module/model:model"),
react_native_target("java/com/facebook/react/modules/core:core"),
@ -38,3 +39,5 @@ rn_android_library(
react_native_target("res:uimanager"),
],
)
# /Users/dvacca/fbsource/fbandroid/xplat_cell/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK

View File

@ -10,12 +10,12 @@ import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.facebook.fbreact.fabricxx.jsi.Binding;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactInstanceManagerBuilder;
@ -29,8 +29,8 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.fabric.jsc.FabricJSCBinding;
import com.facebook.react.fabric.FabricBinder;
import com.facebook.react.fabric.FabricBinding;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionAwareActivity;
import com.facebook.react.modules.core.PermissionListener;
@ -239,7 +239,7 @@ public class ReactAppTestActivity extends FragmentActivity
public JSIModuleProvider getJSIModuleProvider() {
return new JSIModuleProvider() {
@Override
public FabricUIManager get() {
public UIManager get() {
List<ViewManager> viewManagers =
mReactInstanceManager.getOrCreateViewManagers(
reactApplicationContext);
@ -247,14 +247,18 @@ public class ReactAppTestActivity extends FragmentActivity
reactApplicationContext
.getNativeModule(UIManagerModule.class)
.getEventDispatcher();
FabricUIManager fabricUIManager =
new FabricUIManager(
reactApplicationContext,
new ViewManagerRegistry(viewManagers),
jsContext,
eventDispatcher);
new FabricJSCBinding().installFabric(jsContext, fabricUIManager);
return fabricUIManager;
ViewManagerRegistry viewManagerRegistry =
new ViewManagerRegistry(
mReactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
UIManager uiManager =
new com.facebook.fbreact.fabricxx.UIManager(
reactApplicationContext, viewManagerRegistry, jsContext);
FabricBinding binding = new Binding();
binding.installFabric(jsContext, (FabricBinder) uiManager);
return uiManager;
}
};
}

View File

@ -1,158 +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;
import com.facebook.common.logging.FLog;
import com.facebook.debug.holder.PrinterHolder;
import com.facebook.debug.tags.ReactDebugOverlayTags;
import com.facebook.react.common.ArrayUtils;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewAtIndex;
import com.facebook.systrace.Systrace;
import com.facebook.systrace.SystraceMessage;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
public class FabricReconciler {
private static final String TAG = FabricReconciler.class.getSimpleName();
private static final boolean DEBUG = ReactBuildConfig.DEBUG || PrinterHolder
.getPrinter().shouldDisplayLogMessage(ReactDebugOverlayTags.FABRIC_RECONCILER);
private UIViewOperationQueue uiViewOperationQueue;
public FabricReconciler(UIViewOperationQueue uiViewOperationQueue) {
this.uiViewOperationQueue = uiViewOperationQueue;
}
public void manageChildren(ReactShadowNode previousRootShadowNode, ReactShadowNode newRootShadowNode) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricReconciler.manageChildren")
.flush();
try {
List<ReactShadowNode> prevList =
previousRootShadowNode == null ? null : previousRootShadowNode.getChildrenList();
manageChildren(newRootShadowNode, prevList, newRootShadowNode.getChildrenList());
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
private void manageChildren(
ReactShadowNode parent,
@Nullable List<ReactShadowNode> prevList,
@Nullable List<ReactShadowNode> newList) {
prevList = prevList == null ? Collections.<ReactShadowNode>emptyList() : prevList;
newList = newList == null ? Collections.<ReactShadowNode>emptyList() : newList;
// Iterate through each child list and compare each previous and next child. Same nodes
// implies no change is needed. If the nodes are different but are referencing the same view,
// the view needs to be updated with new props and children. Otherwise, there has been
// a change in the children positions.
int sameReactTagIndex = 0;
for (; sameReactTagIndex < Math.min(prevList.size(), newList.size()); sameReactTagIndex++) {
ReactShadowNode prevNode = prevList.get(sameReactTagIndex);
ReactShadowNode newNode = newList.get(sameReactTagIndex);
if (prevNode == newNode) {
continue;
}
if (prevNode.getReactTag() != newNode.getReactTag()) {
break;
}
enqueueUpdateProperties(newNode, prevNode);
manageChildren(prevNode, prevNode.getChildrenList(), newNode.getChildrenList());
newNode.setOriginalReactShadowNode(null);
}
int firstRemovedOrAddedViewIndex = sameReactTagIndex;
// Every ReactShadowNode on the newList that is on the right side of
// firstRemovedOrAddedViewIndex is defined as an added view.
// It is more efficient to reorder removing and adding all the views in the right order, instead
// of calculating the minimum amount of reorder operations.
Set<Integer> addedTags = new HashSet<>();
List<ViewAtIndex> viewsToAdd = new LinkedList<>();
for (int k = firstRemovedOrAddedViewIndex; k < newList.size(); k++) {
ReactShadowNode newNode = newList.get(k);
if (newNode.isVirtual()) continue;
enqueueUpdateProperties(newNode, newNode.getOriginalReactShadowNode());
viewsToAdd.add(new ViewAtIndex(newNode.getReactTag(), k));
List previousChildrenList = newNode.getOriginalReactShadowNode() == null ? null : newNode.getOriginalReactShadowNode().getChildrenList();
manageChildren(newNode, previousChildrenList, newNode.getChildrenList());
newNode.setOriginalReactShadowNode(null);
addedTags.add(newNode.getReactTag());
}
// Every ReactShadowNode on the prevList that is on the right side of
// firstRemovedOrAddedViewIndex is defined as a removed view.
// It is more efficient to reorder removing and adding all the views in the right order, instead
// of calculating the minimum amount of reorder operations.
// If a View is not re-ordered, then the ReactTag is deleted (ReactShadowNode and native View
// are released from memory)
List<Integer> tagsToDelete = new LinkedList<>();
List<Integer> indicesToRemove = new LinkedList<>();
for (int j = prevList.size() - 1; j >= firstRemovedOrAddedViewIndex; j--) {
ReactShadowNode nodeToRemove = prevList.get(j);
if (nodeToRemove.isVirtual()) continue;
indicesToRemove.add(0, j);
if (!addedTags.contains(nodeToRemove.getReactTag())) {
tagsToDelete.add(nodeToRemove.getReactTag());
}
}
// TODO (t27180994): Mutate views synchronously on main thread
if (!(indicesToRemove.isEmpty() && viewsToAdd.isEmpty() && tagsToDelete.isEmpty())) {
int[] indicesToRemoveArray = ArrayUtils.copyListToArray(indicesToRemove);
ViewAtIndex[] viewsToAddArray = viewsToAdd.toArray(new ViewAtIndex[viewsToAdd.size()]);
int[] tagsToDeleteArray = ArrayUtils.copyListToArray(tagsToDelete);
if (DEBUG) {
FLog.d(
TAG,
"manageChildren.enqueueManageChildren parent: " + parent.getReactTag() +
"\n\tIndices2Remove: " + Arrays.toString(indicesToRemoveArray) +
"\n\tViews2Add: " + Arrays.toString(viewsToAddArray) +
"\n\tTags2Delete: " + Arrays.toString(tagsToDeleteArray));
}
uiViewOperationQueue.enqueueManageChildren(
parent.getReactTag(), indicesToRemoveArray, viewsToAddArray, tagsToDeleteArray);
}
}
private void enqueueUpdateProperties(ReactShadowNode newNode, ReactShadowNode prevNode) {
int reactTag = newNode.getReactTag();
if (DEBUG) {
FLog.d(
TAG,
"manageChildren.enqueueUpdateProperties " +
"\n\ttag: " + reactTag +
"\n\tviewClass: " + newNode.getViewClass() +
"\n\tinstanceHandle: " + newNode.getInstanceHandle() +
"\n\tnewProps: " + newNode.getNewProps());
}
if (prevNode != null) {
newNode.updateScreenLayout(prevNode);
}
if (newNode.getNewProps() != null) {
uiViewOperationQueue.enqueueUpdateProperties(
reactTag, newNode.getViewClass(), newNode.getNewProps());
}
uiViewOperationQueue.enqueueUpdateInstanceHandle(
reactTag, newNode.getInstanceHandle());
}
}

View File

@ -1,688 +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;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
import android.os.SystemClock;
import android.view.View;
import com.facebook.common.logging.FLog;
import com.facebook.debug.holder.PrinterHolder;
import com.facebook.debug.tags.ReactDebugOverlayTags;
import com.facebook.infer.annotation.Assertions;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.fabric.events.FabricEventEmitter;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.ReactRootViewTagGenerator;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.react.uimanager.common.MeasureSpecProvider;
import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.systrace.Systrace;
import com.facebook.systrace.SystraceMessage;
import com.facebook.yoga.YogaDirection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* This class is responsible to create, clone and update {@link ReactShadowNode} using the Fabric
* API.
*/
@SuppressWarnings("unused") // used from JNI
@DoNotStrip
public class FabricUIManager implements UIManager, JSHandler, FabricBinder {
private static final String TAG = FabricUIManager.class.getSimpleName();
private static final boolean DEBUG = ReactBuildConfig.DEBUG || PrinterHolder.getPrinter().shouldDisplayLogMessage(ReactDebugOverlayTags.FABRIC_UI_MANAGER);
private final RootShadowNodeRegistry mRootShadowNodeRegistry = new RootShadowNodeRegistry();
private final ReactApplicationContext mReactApplicationContext;
private final ViewManagerRegistry mViewManagerRegistry;
private final UIViewOperationQueue mUIViewOperationQueue;
private final NativeViewHierarchyManager mNativeViewHierarchyManager;
private final JavaScriptContextHolder mJSContext;
private volatile int mCurrentBatch = 0;
private final FabricReconciler mFabricReconciler;
private final EventDispatcher mEventDispatcher;
private FabricBinding mBinding;
private final FabricEventEmitter mFabricEventEmitter;
private long mEventHandlerPointer;
private long mLastCalculateLayoutTime = 0;
public FabricUIManager(
ReactApplicationContext reactContext,
ViewManagerRegistry viewManagerRegistry,
JavaScriptContextHolder jsContext,
EventDispatcher eventDispatcher) {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
mReactApplicationContext = reactContext;
mViewManagerRegistry = viewManagerRegistry;
mNativeViewHierarchyManager = new NativeViewHierarchyManager(viewManagerRegistry);
mUIViewOperationQueue =
new UIViewOperationQueue(
reactContext, mNativeViewHierarchyManager, 0);
mFabricReconciler = new FabricReconciler(mUIViewOperationQueue);
mFabricEventEmitter =
new FabricEventEmitter(mReactApplicationContext, this);
mEventDispatcher = eventDispatcher;
mJSContext = jsContext;
}
@Override
public void setBinding(FabricBinding binding) {
mBinding = binding;
}
/** Creates a new {@link ReactShadowNode} */
@Nullable
@DoNotStrip
public ReactShadowNode createNode(
int reactTag, String viewName, int rootTag, ReadableNativeMap props, long eventTarget) {
if (DEBUG) {
FLog.d(TAG, "createNode \n\ttag: " + reactTag +
"\n\tviewName: " + viewName +
"\n\trootTag: " + rootTag +
"\n\tprops: " + props);
}
try {
ViewManager viewManager = mViewManagerRegistry.get(viewName);
ReactShadowNode node = viewManager.createShadowNodeInstance(mReactApplicationContext);
ReactShadowNode rootNode = getRootNode(rootTag);
node.setRootTag(rootNode.getReactTag());
node.setViewClassName(viewName);
node.setInstanceHandle(eventTarget);
node.setReactTag(reactTag);
node.setThemedContext(rootNode.getThemedContext());
ReactStylesDiffMap styles = updateProps(node, props);
if (!node.isVirtual()) {
mUIViewOperationQueue.enqueueCreateView(
rootNode.getThemedContext(), reactTag, viewName, styles);
}
return node;
} catch (Throwable t) {
handleException(getRootNode(rootTag), t);
return null;
}
}
@VisibleForTesting
ReactShadowNode getRootNode(int rootTag) {
return mRootShadowNodeRegistry.getNode(rootTag);
}
private ReactStylesDiffMap updateProps(ReactShadowNode node, @Nullable ReadableNativeMap props) {
ReactStylesDiffMap styles = null;
if (props != null) {
styles = new ReactStylesDiffMap(props);
node.updateProperties(styles);
}
return styles;
}
/**
* @return a clone of the {@link ReactShadowNode} received by parameter. The cloned
* ReactShadowNode will contain a copy of all the internal data of the original node,
* including its children set (note that the children nodes will not be cloned).
*/
@Nullable
@DoNotStrip
public ReactShadowNode cloneNode(ReactShadowNode node) {
if (DEBUG) {
FLog.d(TAG, "cloneNode \n\tnode: " + node);
}
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.cloneNode")
.flush();
try {
ReactShadowNode clone = node.mutableCopy(node.getInstanceHandle());
assertReactShadowNodeCopy(node, clone);
return clone;
} catch (Throwable t) {
handleException(node, t);
return null;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
/**
* @return a clone of the {@link ReactShadowNode} received by parameter. The cloned
* ReactShadowNode will contain a copy of all the internal data of the original node, but its
* children set will be empty.
*/
@Nullable
@DoNotStrip
public ReactShadowNode cloneNodeWithNewChildren(ReactShadowNode node) {
if (DEBUG) {
FLog.d(TAG, "cloneNodeWithNewChildren \n\tnode: " + node);
}
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.cloneNodeWithNewChildren")
.flush();
try {
ReactShadowNode clone = node.mutableCopyWithNewChildren(node.getInstanceHandle());
assertReactShadowNodeCopy(node, clone);
return clone;
} catch (Throwable t) {
handleException(node, t);
return null;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
/**
* @return a clone of the {@link ReactShadowNode} received by parameter. The cloned
* ReactShadowNode will contain a copy of all the internal data of the original node, but its
* props will be overridden with the {@link ReadableMap} received by parameter.
*/
@Nullable
@DoNotStrip
public ReactShadowNode cloneNodeWithNewProps(
ReactShadowNode node, @Nullable ReadableNativeMap newProps) {
if (DEBUG) {
FLog.d(TAG, "cloneNodeWithNewProps \n\tnode: " + node + "\n\tprops: " + newProps);
}
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.cloneNodeWithNewProps")
.flush();
try {
ReactShadowNode clone = node.mutableCopyWithNewProps(node.getInstanceHandle(),
newProps == null ? null : new ReactStylesDiffMap(newProps));
assertReactShadowNodeCopy(node, clone);
return clone;
} catch (Throwable t) {
handleException(node, t);
return null;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
/**
* @return a clone of the {@link ReactShadowNode} received by parameter. The cloned
* ReactShadowNode will contain a copy of all the internal data of the original node, but its
* props will be overridden with the {@link ReadableMap} received by parameter and its
* children set will be empty.
*/
@Nullable
@DoNotStrip
public ReactShadowNode cloneNodeWithNewChildrenAndProps(
ReactShadowNode node, ReadableNativeMap newProps) {
if (DEBUG) {
FLog.d(TAG, "cloneNodeWithNewChildrenAndProps \n\tnode: " + node + "\n\tnewProps: " + newProps);
}
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.cloneNodeWithNewChildrenAndProps")
.flush();
try {
ReactShadowNode clone =
node.mutableCopyWithNewChildrenAndProps(node.getInstanceHandle(),
newProps == null ? null : new ReactStylesDiffMap(newProps));
assertReactShadowNodeCopy(node, clone);
return clone;
} catch (Throwable t) {
handleException(node, t);
return null;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
private void assertReactShadowNodeCopy(ReactShadowNode source, ReactShadowNode target) {
Assertions.assertCondition(
source.getClass().equals(target.getClass()),
"Found "
+ target.getClass()
+ " class when expecting: "
+ source.getClass()
+ ". Check that "
+ source.getClass()
+ " implements the copy() method correctly.");
}
/**
* Appends the child {@link ReactShadowNode} to the children set of the parent {@link
* ReactShadowNode}.
*/
@Nullable
@DoNotStrip
public void appendChild(ReactShadowNode parent, ReactShadowNode child) {
if (DEBUG) {
FLog.d(TAG, "appendChild \n\tparent: " + parent + "\n\tchild: " + child);
}
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.appendChild")
.flush();
try {
// If the child to append was already committed (child.isSealed()),
// then we add a mutation of it. In the future this will be performed by FabricJS / Fiber.
//TODO: T27926878 avoid cloning shared child
if (child.isSealed()) {
child = child.mutableCopy(child.getInstanceHandle());
}
parent.addChildAt(child, parent.getChildCount());
} catch (Throwable t) {
handleException(parent, t);
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
/**
* @return an empty {@link List<ReactShadowNode>} that will be used to append the {@link
* ReactShadowNode} elements of the root. Typically this List will contain one element.
*/
@DoNotStrip
public List<ReactShadowNode> createChildSet(int rootTag) {
if (DEBUG) {
FLog.d(TAG, "createChildSet rootTag: " + rootTag);
}
return new ArrayList<>(1);
}
/**
* Adds the {@link ReactShadowNode} to the {@link List<ReactShadowNode>} received by parameter.
*/
@DoNotStrip
public void appendChildToSet(List<ReactShadowNode> childList, ReactShadowNode child) {
childList.add(child);
}
@DoNotStrip
public synchronized void completeRoot(int rootTag, @Nullable List<ReactShadowNode> childList) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.completeRoot")
.flush();
try {
long startTime = SystemClock.uptimeMillis();
childList = childList == null ? new LinkedList<ReactShadowNode>() : childList;
if (DEBUG) {
FLog.d(TAG, "completeRoot rootTag: " + rootTag + ", childList: " + childList);
}
ReactShadowNode currentRootShadowNode = getRootNode(rootTag);
Assertions.assertNotNull(
currentRootShadowNode,
"Root view with tag " + rootTag + " must be added before completeRoot is called");
currentRootShadowNode = calculateDiffingAndCreateNewRootNode(currentRootShadowNode, childList);
if (DEBUG) {
FLog.d(
TAG,
"ReactShadowNodeHierarchy after diffing: " + currentRootShadowNode.getHierarchyInfo());
}
applyUpdatesRecursive(currentRootShadowNode);
mUIViewOperationQueue.dispatchViewUpdates(
mCurrentBatch++, startTime, mLastCalculateLayoutTime);
mRootShadowNodeRegistry.replaceNode(currentRootShadowNode);
} catch (Exception e) {
handleException(getRootNode(rootTag), e);
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
public void dispatchCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
mUIViewOperationQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs);
}
private void notifyOnBeforeLayoutRecursive(ReactShadowNode node) {
if (!node.hasUpdates()) {
return;
}
for (int i = 0; i < node.getChildCount(); i++) {
notifyOnBeforeLayoutRecursive(node.getChildAt(i));
}
node.onBeforeLayout();
}
private ReactShadowNode calculateDiffingAndCreateNewRootNode(
ReactShadowNode currentRootShadowNode, List<ReactShadowNode> newChildList) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.calculateDiffingAndCreateNewRootNode")
.flush();
try {
ReactShadowNode newRootShadowNode = currentRootShadowNode.mutableCopyWithNewChildren(currentRootShadowNode.getInstanceHandle());
for (ReactShadowNode child : newChildList) {
appendChild(newRootShadowNode, child);
}
if (DEBUG) {
FLog.d(
TAG,
"ReactShadowNodeHierarchy before calculateLayout: " + newRootShadowNode.getHierarchyInfo());
}
notifyOnBeforeLayoutRecursive(newRootShadowNode);
calculateLayout(newRootShadowNode);
if (DEBUG) {
FLog.d(
TAG,
"ReactShadowNodeHierarchy after calculateLayout: " + newRootShadowNode.getHierarchyInfo());
}
mFabricReconciler.manageChildren(currentRootShadowNode, newRootShadowNode);
return newRootShadowNode;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
private void calculateLayout(ReactShadowNode newRootShadowNode) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.calculateLayout")
.flush();
long startTime = SystemClock.uptimeMillis();
try {
newRootShadowNode.calculateLayout();
} finally{
mLastCalculateLayoutTime = SystemClock.uptimeMillis() - startTime;
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
private void applyUpdatesRecursive(ReactShadowNode node) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager.applyUpdatesRecursive")
.flush();
try {
applyUpdatesRecursive(node, 0, 0);
} finally{
SystraceMessage.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
private void applyUpdatesRecursive(ReactShadowNode node, float absoluteX, float absoluteY) {
if (!node.hasUpdates()) {
return;
}
if (!node.isVirtualAnchor()) {
for (int i = 0; i < node.getChildCount(); i++) {
applyUpdatesRecursive(
node.getChildAt(i),
absoluteX + node.getLayoutX(),
absoluteY + node.getLayoutY());
}
}
int tag = node.getReactTag();
if (getRootNode(tag) == null) {
boolean frameDidChange =
node.dispatchUpdates(absoluteX, absoluteY, mUIViewOperationQueue, null);
// Notify JS about layout event if requested
// and if the position or dimensions actually changed
// (consistent with iOS and Android Default implementation).
if (frameDidChange && node.shouldNotifyOnLayout()) {
mUIViewOperationQueue.enqueueOnLayoutEvent(tag,
node.getScreenX(),
node.getScreenY(),
node.getScreenWidth(),
node.getScreenHeight());
}
}
// Set the reference to the OriginalReactShadowNode to NULL, as the tree is already committed
// and we do not need to hold references to the previous tree anymore
node.setOriginalReactShadowNode(null);
node.markUpdateSeen();
node.markAsSealed();
}
@Override
@DoNotStrip
public <T extends SizeMonitoringFrameLayout & MeasureSpecProvider> int addRootView(
final T rootView) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricUIManager.addRootView")
.flush();
try {
final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag();
ThemedReactContext themedRootContext =
new ThemedReactContext(mReactApplicationContext, rootView.getContext());
ReactShadowNode rootShadowNode = createRootShadowNode(rootTag, themedRootContext);
int widthMeasureSpec = rootView.getWidthMeasureSpec();
int heightMeasureSpec = rootView.getHeightMeasureSpec();
updateRootView(rootShadowNode, widthMeasureSpec, heightMeasureSpec);
rootView.setOnSizeChangedListener(
new SizeMonitoringFrameLayout.OnSizeChangedListener() {
@Override
public void onSizeChanged(final int width, final int height, int oldW, int oldH) {
updateRootSize(rootTag, width, height);
}
});
mRootShadowNodeRegistry.registerNode(rootShadowNode);
mUIViewOperationQueue.addRootView(rootTag, rootView, themedRootContext);
return rootTag;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
@Override
@DoNotStrip
public synchronized void updateRootLayoutSpecs(final int rootViewTag, final int widthMeasureSpec, final int heightMeasureSpec) {
mReactApplicationContext.runOnNativeModulesQueueThread(new Runnable() {
@Override
public void run() {
ReactShadowNode rootNode = getRootNode(rootViewTag);
if (rootNode == null) {
FLog.w(ReactConstants.TAG, "Tried to update non-existent root tag: " + rootViewTag);
return;
}
ReactShadowNode newRootNode = rootNode.mutableCopy(rootNode.getInstanceHandle());
updateRootView(newRootNode, widthMeasureSpec, heightMeasureSpec);
mRootShadowNodeRegistry.replaceNode(newRootNode);
}
});
}
/**
* Updates the root view size and re-render the RN surface.
*
* //TODO: change synchronization to integrate with new #render loop.
*/
private synchronized void updateRootSize(int rootTag, int newWidth, int newHeight) {
ReactShadowNode rootNode = getRootNode(rootTag);
if (rootNode == null) {
FLog.w(
ReactConstants.TAG,
"Tried to update size of non-existent tag: " + rootTag);
return;
}
ReactShadowNode newRootNode = rootNode.mutableCopy(rootNode.getInstanceHandle());
int newWidthSpec = View.MeasureSpec.makeMeasureSpec(newWidth, View.MeasureSpec.EXACTLY);
int newHeightSpec = View.MeasureSpec.makeMeasureSpec(newHeight, View.MeasureSpec.EXACTLY);
updateRootView(newRootNode, newWidthSpec, newHeightSpec);
completeRoot(rootTag, newRootNode.getChildrenList());
}
public void removeRootView(int rootTag) {
mUIViewOperationQueue.enqueueRemoveRootView(rootTag);
mRootShadowNodeRegistry.removeNode(rootTag);
}
private ReactShadowNode createRootShadowNode(int rootTag, ThemedReactContext themedReactContext) {
ReactShadowNode rootNode = new ReactShadowNodeImpl();
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
if (sharedI18nUtilInstance.isRTL(themedReactContext)) {
rootNode.setLayoutDirection(YogaDirection.RTL);
}
rootNode.setViewClassName("Root");
rootNode.setReactTag(rootTag);
rootNode.setThemedContext(themedReactContext);
return rootNode;
}
/**
* Updates the styles of the {@link ReactShadowNode} based on the Measure specs received by
* parameters.
*/
private void updateRootView(
ReactShadowNode node, int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
int widthSize = View.MeasureSpec.getSize(widthMeasureSpec);
switch (widthMode) {
case EXACTLY:
node.setStyleWidth(widthSize);
break;
case AT_MOST:
node.setStyleMaxWidth(widthSize);
break;
case UNSPECIFIED:
node.setStyleWidthAuto();
break;
}
int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);
switch (heightMode) {
case EXACTLY:
node.setStyleHeight(heightSize);
break;
case AT_MOST:
node.setStyleMaxHeight(heightSize);
break;
case UNSPECIFIED:
node.setStyleHeightAuto();
break;
}
}
private void handleException(ReactShadowNode node, Throwable t) {
try {
ThemedReactContext context = node.getThemedContext();
// TODO move exception management to JNI side, and refactor to avoid wrapping Throwable into
// a RuntimeException
context.handleException(new RuntimeException(t));
} catch (Exception ex) {
FLog.e(TAG, "Exception while executing a Fabric method", t);
throw new RuntimeException(ex.getMessage(), t);
}
}
@Nullable
@DoNotStrip
public long getEventTarget(int reactTag) {
long instanceHandle = mNativeViewHierarchyManager.getInstanceHandle(reactTag);
return instanceHandle;
}
@DoNotStrip
public void registerEventHandler(long eventHandlerPointer) {
mEventHandlerPointer = eventHandlerPointer;
}
@DoNotStrip
public void releaseEventTarget(long eventTargetPointer) {
mBinding.releaseEventTarget(mJSContext.get(), eventTargetPointer);
}
@DoNotStrip
public void releaseEventHandler(long eventHandlerPointer) {
mBinding.releaseEventHandler(mJSContext.get(), eventHandlerPointer);
}
@Override
@DoNotStrip
public void invoke(long eventTarget, String name, WritableMap params) {
if (DEBUG) {
FLog.d(
TAG,
"Dispatching event for target: " + eventTarget);
}
if (params == null) {
params = new WritableNativeMap();
}
mBinding.dispatchEventToTarget(mJSContext.get(), mEventHandlerPointer, eventTarget, name, (WritableNativeMap) params);
}
@Override
public void setJSResponder(int reactTag, boolean blockNativeResponder) {
// TODO: Do nothing for now
}
@Override
public void clearJSResponder() {
// TODO: Do nothing for now
}
@Override
public void initialize() {
FabricEventEmitter eventEmitter =
new FabricEventEmitter(mReactApplicationContext, this);
mEventDispatcher.registerEventEmitter(FABRIC, mFabricEventEmitter);
}
@Override
public void onCatalystInstanceDestroy() {
mBinding.releaseEventHandler(mJSContext.get(), mEventHandlerPointer);
mEventDispatcher.unregisterEventEmitter(FABRIC);
mFabricEventEmitter.close();
}
@Override
public void profileNextBatch() {
mUIViewOperationQueue.profileNextBatch();
}
@Override
public Map<String, Long> getPerformanceCounters() {
// TODO change profiling when enabling multi-thread rendering.
return mUIViewOperationQueue.getProfiledBatchPerfCounters();
}
}

View File

@ -1,15 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.fabric;
import com.facebook.react.bridge.WritableMap;
public interface JSHandler {
void invoke(long instanceHandle, String name, WritableMap params);
}

View File

@ -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;
import com.facebook.react.uimanager.ReactShadowNode;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.concurrent.ThreadSafe;
/**
* Simple container class to keep track of {@link ReactShadowNode}s that represents the Root
* Shadow Nodes of a {@link FabricUIManager}.
*/
@ThreadSafe
public class RootShadowNodeRegistry {
private final ConcurrentHashMap<Integer, ReactShadowNode> mTagsToRootNodes = new ConcurrentHashMap<>();
/**
* Registers the {@link ReactShadowNode} received as a parameter as a RootShadowNode.
*/
public synchronized void registerNode(ReactShadowNode node) {
mTagsToRootNodes.put(node.getReactTag(), node);
}
/**
* Register the {@link ReactShadowNode} received as a parameter as a RootShadowNode, replacing
* the previous RootShadowNode associated for the {@link ReactShadowNode#getReactTag()}
*/
public void replaceNode(ReactShadowNode node) {
mTagsToRootNodes.replace(node.getReactTag(), node);
}
public void removeNode(Integer tag) {
mTagsToRootNodes.remove(tag);
}
public ReactShadowNode getNode(int tag) {
return mTagsToRootNodes.get(tag);
}
}

View File

@ -1,95 +0,0 @@
package com.facebook.react.fabric;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReactContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* This class allows Native code to schedule work in JS.
* Work (following JS naming) represents a task that needs to be executed in JS.
*
* Four types of work are supported by this class:
*
* Synchronous:
* - Sync work -> flushSync()
* - Work work -> flushSerial()
*
* Asynchronous:
* - Interactive work (serial): -> scheduleSerial()
* - Deferred work: -> scheduleWork()
*
*/
public class Scheduler {
private static final String TAG = Scheduler.class.getSimpleName();
// The usage of this executor might change in the near future.
private final ExecutorService mExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
private final ReactContext mReactContext;
public Scheduler(ReactContext reactContext) {
mReactContext = reactContext;
}
/**
* This method schedules work to be executed with the lowest priority in the JS Thread.
*
* The current implementation queues "work"s in an unbounded queue tight to a SingleThreadExecutor.
* Work objects are going to be submitted one-by-one at the end of the JS Queue Thread.
*
* Notice that the current implementation might experience some delays in JS work execution,
* depending on the size of the JS Queue and the time it takes to execute each work in JS.
*
* TODO: This implementation is very likely to change in the near future.
*/
public void scheduleWork(final Work work) {
try {
mExecutor.execute(new Runnable() {
@Override
public void run() {
mReactContext.runOnJSQueueThread(new Runnable() {
@Override
public void run() {
try {
work.run();
} catch (Exception ex) {
FLog.w(TAG, "Exception running work in JS.", ex);
throw ex;
}
}
});
}
});
} catch (RejectedExecutionException ex) {
// This can happen if a Work is scheduled when the Scheduler is being shutdown.
// For now, we log and do not take any action.
FLog.i(TAG, "Unable to schedule task.");
}
}
public void flushSync(Work work) {
// TODO T26717866 this method needs to be implemented. The current implementation is just for
// testing purpose.
}
public void flushSerial(Work work) {
// TODO T26717866 this method needs to be implemented. The current implementation is just for
// testing purpose.
}
public void scheduleSerial(Work work) {
// TODO T26717866 this method needs to be implemented. The current implementation is just for
// testing purpose.
}
/**
* Shutdowns the {@link Scheduler}. this operation will attempt to stop all "active executing"
* Works items and it will "halts:" all the Works items waiting to be executed.
*/
public void shutdown() {
mExecutor.shutdownNow();
}
}

View File

@ -1,20 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.fabric;
/**
* Interface that represents a task or piece of code that will be executed by {@link Scheduler}
* This follows React API naming for consistency.
*/
public interface Work {
void run();
}

View File

@ -1,183 +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.events;
import static com.facebook.react.uimanager.events.TouchesHelper.CHANGED_TOUCHES_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_CANCEL_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_END_KEY;
import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.Pair;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableMap;
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.fabric.FabricUIManager;
import com.facebook.react.fabric.Scheduler;
import com.facebook.react.fabric.Work;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import java.io.Closeable;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
@TargetApi(Build.VERSION_CODES.ECLAIR)
public class FabricEventEmitter implements RCTEventEmitter, Closeable {
private static final String TAG = FabricEventEmitter.class.getSimpleName();
private final FabricUIManager mFabricUIManager;
private final Scheduler mScheduler;
public FabricEventEmitter(ReactApplicationContext context, FabricUIManager fabricUIManager) {
mScheduler = new Scheduler(context);
mFabricUIManager = fabricUIManager;
}
@Override
public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap params) {
try {
long eventTarget = mFabricUIManager.getEventTarget(reactTag);
mScheduler.scheduleWork(new FabricUIManagerWork(eventTarget, eventName, params));
} catch (IllegalViewOperationException e) {
FLog.e(TAG, "Unable to emmit event for tag " + reactTag, e);
}
}
@Override
public void close() {
mScheduler.shutdown();
}
private class FabricUIManagerWork implements Work {
private final long mEventTarget;
private final String mEventName;
private final WritableMap mParams;
public FabricUIManagerWork(long eventTarget, String eventName, @Nullable WritableMap params) {
mEventTarget = eventTarget;
mEventName = eventName;
mParams = params;
}
@Override
public void run() {
try {
mFabricUIManager.invoke(mEventTarget, mEventName, mParams);
} catch (Throwable t) {
FLog.e(TAG, "Error sending event " + mEventName, t);
//TODO: manage exception properly
} finally{
// TODO(dvacca): We need to only release this after all shadow nodes
// have been released. The easiest way would be to adopt the event
// emitter approach from the C++ Fabric. For now, we'll just leak.
// mFabricUIManager.releaseEventTarget(mEventTarget);
}
}
}
@Override
public void receiveTouches(String eventTopLevelType, WritableArray touches,
WritableArray changedIndices) {
Pair<WritableArray, WritableArray> result =
TOP_TOUCH_END_KEY.equalsIgnoreCase(eventTopLevelType) ||
TOP_TOUCH_CANCEL_KEY.equalsIgnoreCase(eventTopLevelType)
? removeTouchesAtIndices(touches, changedIndices)
: touchSubsequence(touches, changedIndices);
WritableArray changedTouches = result.first;
touches = result.second;
for (int jj = 0; jj < changedTouches.size(); jj++) {
WritableMap touch = getWritableMap(changedTouches.getMap(jj));
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.putArray(CHANGED_TOUCHES_KEY, changedTouches);
touch.putArray(TOUCHES_KEY, touches);
WritableMap nativeEvent = touch;
int rootNodeID = 0;
int target = nativeEvent.getInt(TARGET_KEY);
if (target < 1) {
FLog.e(TAG,"A view is reporting that a touch occurred on tag zero.");
} else {
rootNodeID = target;
}
receiveEvent(rootNodeID, eventTopLevelType, touch);
}
}
/**
* Destroys `touches` by removing touch objects at indices `indices`. This is
* to maintain compatibility with W3C touch "end" events, where the active
* touches don't include the set that has just been "ended".
*
* This method was originally in ReactNativeRenderer.js
*
* TODO: this method is a copy from ReactNativeRenderer.removeTouchesAtIndices and it needs
* to be rewritten in a more efficient way,
*
* @param touches {@link WritableArray} Deserialized touch objects.
* @param indices {WritableArray} Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
private Pair<WritableArray, WritableArray> removeTouchesAtIndices(WritableArray touches, WritableArray indices) {
WritableArray rippedOut = new WritableNativeArray();
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
WritableArray tempTouches = new WritableNativeArray();
Set<Integer> rippedOutIndices = new HashSet<>();
for (int i = 0; i < indices.size(); i++) {
int index = indices.getInt(i);
rippedOut.pushMap(getWritableMap(touches.getMap(index)));
rippedOutIndices.add(index);
}
for (int j = 0 ; j < touches.size() ; j++) {
if (!rippedOutIndices.contains(j)) {
tempTouches.pushMap(getWritableMap(touches.getMap(j)));
}
}
return new Pair<>(rippedOut, tempTouches);
}
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
*
* This method was originally in ReactNativeRenderer.js
*
* @param touches {@link WritableArray} Deserialized touch objects.
* @param changedIndices {@link WritableArray} Indices by which to pull subsequence.
* @return {Array<Touch>} Subsequence of touch objects.
*/
private Pair<WritableArray, WritableArray> touchSubsequence(WritableArray touches, WritableArray changedIndices) {
WritableArray result = new WritableNativeArray();
for (int i = 0; i < changedIndices.size(); i++) {
result.pushMap(getWritableMap(touches.getMap(i)));
}
return new Pair<>(result, touches);
}
/**
* TODO: this is required because the WritableNativeArray.getMap() returns a ReadableMap instead
* of the original writableMap. this will change in the near future.
*
* @param readableMap {@link ReadableMap} source map
*/
private WritableMap getWritableMap(ReadableMap readableMap) {
WritableNativeMap map = new WritableNativeMap();
map.merge(readableMap);
return map;
}
}

View File

@ -1,24 +0,0 @@
load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library")
rn_android_library(
name = "jsc",
srcs = glob(["**/*.java"]),
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
],
required_for_source_only_abi = True,
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/fabric:fabric"),
react_native_target("java/com/facebook/react/fabric/jsc/jni:jni"),
],
exported_deps = [
react_native_dep("java/com/facebook/jni:jni"),
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
],
)

View File

@ -1,67 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.fabric.jsc;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.FabricBinder;
import com.facebook.react.fabric.FabricBinding;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.bridge.NativeMap;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class FabricJSCBinding implements FabricBinding {
static {
SoLoader.loadLibrary("fabricjscjni");
}
// used from native
@SuppressWarnings("unused")
private final HybridData mHybridData;
private static native HybridData initHybrid();
@Override
public native void releaseEventTarget(long jsContextNativePointer, long eventTargetPointer);
@Override
public native void releaseEventHandler(long jsContextNativePointer, long eventHandlerPointer);
@Override
public native void dispatchEventToEmptyTarget(
long jsContextNativePointer,
long eventHandlerPointer,
String type,
NativeMap payload
);
@Override
public native void dispatchEventToTarget(
long jsContextNativePointer,
long eventHandlerPointer,
long eventTargetPointer,
String type,
NativeMap payload
);
private native void installFabric(long jsContextNativePointer, Object fabricModule);
public FabricJSCBinding() {
mHybridData = initHybrid();
}
@Override
public void installFabric(JavaScriptContextHolder jsContext, FabricBinder fabricModule) {
fabricModule.setBinding(this);
installFabric(jsContext.get(), fabricModule);
}
}

View File

@ -1,21 +0,0 @@
load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "FBJNI_TARGET", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library")
rn_xplat_cxx_library(
name = "jni",
srcs = glob(["*.cpp"]),
headers = glob(["*.h"]),
compiler_flags = [
"-Wall",
"-fexceptions",
"-std=c++1y",
],
platforms = ANDROID,
soname = "libfabricjscjni.$(ext)",
visibility = ["PUBLIC"],
deps = [
FBJNI_TARGET,
"xplat//folly:molly",
react_native_xplat_target("jschelpers:jschelpers"),
react_native_target("jni/react/jni:jni"),
],
)

View File

@ -1,415 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "FabricJSCBinding.h"
#include <fb/fbjni.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Unicode.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
namespace {
bool useCustomJSC = false;
struct JList : public JavaClass<JList> {
static constexpr auto kJavaDescriptor = "Ljava/util/List;";
};
struct JShadowNode : public JavaClass<JShadowNode> {
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/uimanager/ReactShadowNode;";
};
typedef struct FabricJSCUIManager {
FabricJSCUIManager(alias_ref<jobject> module, JSClassRef classRef, bool customJSC)
: wrapperObjectClassRef(classRef)
, useCustomJSC(customJSC) {
fabricUiManager = make_global(module);
JSC_JSClassRetain(useCustomJSC, wrapperObjectClassRef);
}
global_ref<jobject> fabricUiManager;
JSClassRef wrapperObjectClassRef;
bool useCustomJSC;
~FabricJSCUIManager() {
JSC_JSClassRelease(useCustomJSC, wrapperObjectClassRef);
}
} FabricJSCUIManager;
jobject makePlainGlobalRef(jobject object) {
// When storing the global reference we need it to be a plain
// pointer. That's why we use plain jni instead of fbjni here.
return Environment::current()->NewGlobalRef(object);
}
local_ref<JString> JSValueToJString(JSContextRef ctx, JSValueRef value) {
JSStringRef strRef = JSC_JSValueToStringCopy(ctx, value, NULL);
const size_t size = JSStringGetMaximumUTF8CStringSize(strRef);
char buffer[size];
JSStringGetUTF8CString(strRef, buffer, size);
JSC_JSStringRelease(ctx, strRef);
return make_jstring(buffer);
}
local_ref<JShadowNode> JSValueToJShadowNode(JSContextRef ctx, JSValueRef value) {
JSObjectRef obj = JSC_JSValueToObject(ctx, value, NULL);
auto node = static_cast<JShadowNode::javaobject>(JSC_JSObjectGetPrivate(useCustomJSC, obj));
return make_local(node);
}
local_ref<JList> JSValueToJList(JSContextRef ctx, JSValueRef value) {
JSObjectRef obj = JSC_JSValueToObject(ctx, value, NULL);
auto node = static_cast<JList::javaobject>(JSC_JSObjectGetPrivate(useCustomJSC, obj));
return make_local(node);
}
local_ref<ReadableNativeMap::jhybridobject> JSValueToReadableMapViaJSON(JSContextRef ctx, JSValueRef value) {
JSStringRef jsonRef = JSC_JSValueCreateJSONString(ctx, value, 0, NULL);
size_t size = JSC_JSStringGetLength(ctx, jsonRef);
const JSChar* utf16 = JSC_JSStringGetCharactersPtr(ctx, jsonRef);
std::string json = unicode::utf16toUTF8(utf16, size);
JSC_JSStringRelease(ctx, jsonRef);
folly::dynamic dynamicValue = folly::parseJson(json);
return ReadableNativeMap::newObjectCxxArgs(std::move(dynamicValue));
}
JSValueRef ReadableMapToJSValueViaJSON(JSContextRef ctx, NativeMap *map) {
folly::dynamic dynamicValue = map->consume();
auto json = folly::toJson(dynamicValue);
JSStringRef jsonRef = JSC_JSStringCreateWithUTF8CString(ctx, json.c_str());
auto value = JSC_JSValueMakeFromJSONString(ctx, jsonRef);
JSC_JSStringRelease(ctx, jsonRef);
return value;
}
JSValueRef createNode(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;
static auto createNode =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(jint, jstring, jint, ReadableNativeMap::javaobject, jlong)>("createNode");
int reactTag = (int)JSC_JSValueToNumber(ctx, arguments[0], NULL);
auto viewName = JSValueToJString(ctx, arguments[1]);
int rootTag = (int)JSC_JSValueToNumber(ctx, arguments[2], NULL);
auto props = JSC_JSValueIsNull(ctx, arguments[3]) ? local_ref<ReadableNativeMap::jhybridobject>(nullptr) :
JSValueToReadableMapViaJSON(ctx, arguments[3]);;
auto eventTarget = (void *)arguments[4];
auto node = createNode(manager, reactTag, viewName.get(), rootTag, props.get(), (jlong)eventTarget);
return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(node.get()));
}
JSValueRef cloneNode(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;
static auto cloneNode =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject)>("cloneNode");
auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto newNode = cloneNode(manager, previousNode.get());
return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}
JSValueRef cloneNodeWithNewChildren(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;
static auto cloneNodeWithNewChildren =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject)>("cloneNodeWithNewChildren");
auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto newNode = cloneNodeWithNewChildren(manager, previousNode.get());
return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}
JSValueRef cloneNodeWithNewProps(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;
static auto cloneNodeWithNewProps =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject, ReadableNativeMap::javaobject)>("cloneNodeWithNewProps");
auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto props = JSValueToReadableMapViaJSON(ctx, arguments[1]);
auto newNode = cloneNodeWithNewProps(manager, previousNode.get(), props.get());
return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}
JSValueRef cloneNodeWithNewChildrenAndProps(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;
static auto cloneNodeWithNewChildrenAndProps =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JShadowNode>(JShadowNode::javaobject, ReadableNativeMap::javaobject)>("cloneNodeWithNewChildrenAndProps");
auto previousNode = JSValueToJShadowNode(ctx, arguments[0]);
auto props = JSValueToReadableMapViaJSON(ctx, arguments[1]);
auto newNode = cloneNodeWithNewChildrenAndProps(manager, previousNode.get(), props.get());
return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(newNode.get()));
}
JSValueRef appendChild(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
static auto appendChild =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(JShadowNode::javaobject, JShadowNode::javaobject)>("appendChild");
auto parentNode = JSValueToJShadowNode(ctx, arguments[0]);
auto childNode = JSValueToJShadowNode(ctx, arguments[1]);
appendChild(manager, parentNode.get(), childNode.get());
return JSC_JSValueMakeUndefined(ctx);
}
JSValueRef createChildSet(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
JSClassRef classRef = managerWrapper->wrapperObjectClassRef;
static auto createChildSet =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<alias_ref<JList>(jint)>("createChildSet");
int rootTag = (int)JSC_JSValueToNumber(ctx, arguments[0], NULL);
auto childSet = createChildSet(manager, rootTag);
return JSC_JSObjectMake(ctx, classRef, makePlainGlobalRef(childSet.get()));
}
JSValueRef appendChildToSet(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
static auto appendChildToSet =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(JList::javaobject, JShadowNode::javaobject)>("appendChildToSet");
auto childSet = JSValueToJList(ctx, arguments[0]);
auto childNode = JSValueToJShadowNode(ctx, arguments[1]);
appendChildToSet(manager, childSet.get(), childNode.get());
return JSC_JSValueMakeUndefined(ctx);
}
JSValueRef completeRoot(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
static auto completeRoot =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(jint, JList::javaobject)>("completeRoot");
int rootTag = (int)JSC_JSValueToNumber(ctx, arguments[0], NULL);
auto childSet = JSValueToJList(ctx, arguments[1]);
completeRoot(manager, rootTag, childSet.get());
return JSC_JSValueMakeUndefined(ctx);
}
JSValueRef registerEventHandler(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, function);
alias_ref<jobject> manager = managerWrapper->fabricUiManager;
static auto registerEventHandler =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<void(jlong)>("registerEventHandler");
auto eventHandler = arguments[0];
JSC_JSValueProtect(ctx, eventHandler);
registerEventHandler(manager, (jlong)eventHandler);
return JSC_JSValueMakeUndefined(ctx);
}
void finalizeJNIObject(JSObjectRef object) {
// Release whatever global ref object we're storing here.
jobject globalRef = (jobject)JSC_JSObjectGetPrivate(useCustomJSC, object);
Environment::current()->DeleteGlobalRef(globalRef);
}
void finalizeWrapper(JSObjectRef object) {
FabricJSCUIManager *managerWrapper = (FabricJSCUIManager *)JSC_JSObjectGetPrivate(useCustomJSC, object);
delete managerWrapper;
}
void addFabricMethod(
JSContextRef context,
jni::alias_ref<jobject> fabricModule,
JSClassRef nodeClassRef,
JSObjectRef module,
const char *name,
JSObjectCallAsFunctionCallback callback
) {
JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.callAsFunction = callback;
definition.finalize = finalizeWrapper;
JSClassRef classRef = JSC_JSClassCreate(useCustomJSC, &definition);
FabricJSCUIManager *managerWrapper = new FabricJSCUIManager(fabricModule, nodeClassRef, useCustomJSC);
JSObjectRef functionRef = JSC_JSObjectMake(context, classRef, managerWrapper);
JSC_JSClassRelease(useCustomJSC, classRef);
JSStringRef nameStr = JSC_JSStringCreateWithUTF8CString(context, name);
JSC_JSObjectSetProperty(context, module, nameStr, functionRef, kJSPropertyAttributeNone, NULL);
JSC_JSStringRelease(context, nameStr);
}
}
jni::local_ref<FabricJSCBinding::jhybriddata> FabricJSCBinding::initHybrid(
jni::alias_ref<jclass>) {
return makeCxxInstance();
}
void FabricJSCBinding::releaseEventTarget(
jlong jsContextNativePointer,
jlong eventTargetPointer
) {
// This is now a noop.
}
void FabricJSCBinding::releaseEventHandler(
jlong jsContextNativePointer,
jlong eventHandlerPointer
) {
JSContextRef context = (JSContextRef)jsContextNativePointer;
JSValueRef value = (JSValueRef)((void *)eventHandlerPointer);
// Release this function.
JSC_JSValueUnprotect(context, value);
}
void FabricJSCBinding::dispatchEventToEmptyTarget(
jlong jsContextNativePointer,
jlong eventHandlerPointer,
std::string type,
NativeMap *payloadMap
) {
JSContextRef context = (JSContextRef)jsContextNativePointer;
JSObjectRef eventHandler = (JSObjectRef)((void *)eventHandlerPointer);
JSValueRef eventTarget = JSC_JSValueMakeNull(context);
JSObjectRef thisArg = (JSObjectRef)JSC_JSValueMakeUndefined(context);
JSStringRef typeStr = JSC_JSStringCreateWithUTF8CString(context, type.c_str());
JSValueRef typeRef = JSC_JSValueMakeString(context, typeStr);
JSC_JSStringRelease(context, typeStr);
JSValueRef payloadRef = ReadableMapToJSValueViaJSON(context, payloadMap);
JSValueRef args[] = {eventTarget, typeRef, payloadRef};
JSValueRef exn;
JSValueRef result = JSC_JSObjectCallAsFunction(
context,
eventHandler,
thisArg,
3,
args,
&exn
);
if (!result) {
// TODO: Handle error in exn
}
}
void FabricJSCBinding::dispatchEventToTarget(
jlong jsContextNativePointer,
jlong eventHandlerPointer,
jlong eventTargetPointer,
std::string type,
NativeMap *payloadMap
) {
JSContextRef context = (JSContextRef)jsContextNativePointer;
JSObjectRef eventHandler = (JSObjectRef)((void *)eventHandlerPointer);
JSObjectRef eventTarget = (JSObjectRef)((void *)eventTargetPointer);
JSObjectRef thisArg = (JSObjectRef)JSC_JSValueMakeUndefined(context);
JSStringRef typeStr = JSC_JSStringCreateWithUTF8CString(context, type.c_str());
JSValueRef typeRef = JSC_JSValueMakeString(context, typeStr);
JSC_JSStringRelease(context, typeStr);
JSValueRef payloadRef = ReadableMapToJSValueViaJSON(context, payloadMap);
JSValueRef args[] = {eventTarget, typeRef, payloadRef};
JSValueRef exn;
JSValueRef result = JSC_JSObjectCallAsFunction(
context,
eventHandler,
thisArg,
3,
args,
&exn
);
if (!result) {
// TODO: Handle error in exn
}
}
void FabricJSCBinding::installFabric(jlong jsContextNativePointer,
jni::alias_ref<jobject> fabricModule) {
JSContextRef context = (JSContextRef)jsContextNativePointer;
useCustomJSC = facebook::react::isCustomJSCPtr(context);
JSObjectRef module = JSC_JSObjectMake(context, NULL, NULL);
// Class definition for wrapper objects around nodes and sets
JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.finalize = finalizeJNIObject;
JSClassRef classRef = JSC_JSClassCreate(useCustomJSC, &definition);
addFabricMethod(context, fabricModule, classRef, module, "createNode", createNode);
addFabricMethod(context, fabricModule, classRef, module, "cloneNode", cloneNode);
addFabricMethod(context, fabricModule, classRef, module, "cloneNodeWithNewChildren", cloneNodeWithNewChildren);
addFabricMethod(context, fabricModule, classRef, module, "cloneNodeWithNewProps", cloneNodeWithNewProps);
addFabricMethod(context, fabricModule, classRef, module, "cloneNodeWithNewChildrenAndProps", cloneNodeWithNewChildrenAndProps);
addFabricMethod(context, fabricModule, classRef, module, "appendChild", appendChild);
addFabricMethod(context, fabricModule, classRef, module, "createChildSet", createChildSet);
addFabricMethod(context, fabricModule, classRef, module, "appendChildToSet", appendChildToSet);
addFabricMethod(context, fabricModule, classRef, module, "completeRoot", completeRoot);
addFabricMethod(context, fabricModule, classRef, module, "registerEventHandler", registerEventHandler);
JSC_JSClassRelease(useCustomJSC, classRef);
JSObjectRef globalObject = JSC_JSContextGetGlobalObject(context);
JSStringRef globalName = JSC_JSStringCreateWithUTF8CString(context, "nativeFabricUIManager");
JSC_JSObjectSetProperty(context, globalObject, globalName, module, kJSPropertyAttributeNone, NULL);
JSC_JSStringRelease(context, globalName);
}
void FabricJSCBinding::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", FabricJSCBinding::initHybrid),
makeNativeMethod("installFabric", FabricJSCBinding::installFabric),
makeNativeMethod("releaseEventTarget", FabricJSCBinding::releaseEventTarget),
makeNativeMethod("releaseEventHandler", FabricJSCBinding::releaseEventHandler),
makeNativeMethod("dispatchEventToEmptyTarget", FabricJSCBinding::dispatchEventToEmptyTarget),
makeNativeMethod("dispatchEventToTarget", FabricJSCBinding::dispatchEventToTarget),
});
}
}
}

View File

@ -1,54 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <fb/fbjni.h>
#include <react/jni/ReadableNativeMap.h>
namespace facebook {
namespace react {
class Instance;
class FabricJSCBinding : public jni::HybridClass<FabricJSCBinding> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/fabric/jsc/FabricJSCBinding;";
static void registerNatives();
private:
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
void releaseEventTarget(jlong jsContextNativePointer, jlong eventTargetPointer);
void releaseEventHandler(jlong jsContextNativePointer, jlong eventHandlerPointer);
void dispatchEventToEmptyTarget(
jlong jsContextNativePointer,
jlong eventHandlerPointer,
std::string type,
NativeMap *payload
);
void dispatchEventToTarget(
jlong jsContextNativePointer,
jlong eventHandlerPointer,
jlong eventTargetPointer,
std::string type,
NativeMap *payload
);
void installFabric(jlong jsContextNativePointer, jni::alias_ref<jobject> fabricModule);
};
}
}

View File

@ -1,17 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fb/fbjni.h>
#include <fb/xplat_init.h>
#include "FabricJSCBinding.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::xplat::initialize(vm, [] {
facebook::react::FabricJSCBinding::registerNatives();
});
}

View File

@ -75,16 +75,6 @@ public class LayoutShadowNode extends ReactShadowNodeImpl {
mTempYogaValue = new MutableYogaValue();
}
protected LayoutShadowNode(LayoutShadowNode node) {
super(node);
mTempYogaValue = new MutableYogaValue(node.mTempYogaValue);
}
@Override
protected LayoutShadowNode copy() {
return new LayoutShadowNode(this);
}
@ReactProp(name = ViewProps.WIDTH)
public void setWidth(Dynamic width) {
if (isVirtual()) {

View File

@ -68,17 +68,6 @@ public interface ReactShadowNode<T extends ReactShadowNode> {
*/
boolean isYogaLeafNode();
/**
* @return a mutable copy of the {@link ReactShadowNode}
*/
T mutableCopy(long instanceHandle);
T mutableCopyWithNewProps(long instanceHandle, @Nullable ReactStylesDiffMap newProps);
T mutableCopyWithNewChildren(long instanceHandle);
T mutableCopyWithNewChildrenAndProps(long instanceHandle, @Nullable ReactStylesDiffMap newProps);
String getViewClass();
boolean hasUpdates();
@ -366,28 +355,5 @@ public interface ReactShadowNode<T extends ReactShadowNode> {
*/
List<ReactShadowNode> getChildrenList();
/**
* @return the {@link ReactShadowNode} that was used during the cloning mechanism to create
* this {@link ReactShadowNode} or null if this object was not created using a clone operation.
*/
@Nullable ReactShadowNode getOriginalReactShadowNode();
void setOriginalReactShadowNode(@Nullable ReactShadowNode node);
long getInstanceHandle();
void setInstanceHandle(long instanceHandle);
/**
* Mark this {@link ReactShadowNode} as sealed. This means that the node was already committed
* and it should not be updated anymore.
*/
void markAsSealed();
/**
* @return a {@link boolean} that represents if the {@link ReactShadowNode} is sealed.
*/
boolean isSealed();
void updateScreenLayout(ReactShadowNode prevNode);
}

View File

@ -8,14 +8,11 @@ package com.facebook.react.uimanager;
import static java.lang.System.arraycopy;
import com.facebook.common.logging.FLog;
import com.facebook.debug.holder.PrinterHolder;
import com.facebook.debug.tags.ReactDebugOverlayTags;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
import com.facebook.systrace.Systrace;
import com.facebook.systrace.SystraceMessage;
import com.facebook.yoga.YogaAlign;
import com.facebook.yoga.YogaBaselineFunction;
import com.facebook.yoga.YogaConfig;
@ -27,7 +24,6 @@ import com.facebook.yoga.YogaFlexDirection;
import com.facebook.yoga.YogaJustify;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaNodeCloneFunction;
import com.facebook.yoga.YogaOverflow;
import com.facebook.yoga.YogaPositionType;
import com.facebook.yoga.YogaValue;
@ -67,38 +63,9 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
private static final boolean DEBUG = ReactBuildConfig.DEBUG || PrinterHolder.getPrinter().shouldDisplayLogMessage(ReactDebugOverlayTags.FABRIC_UI_MANAGER);
private static final String TAG = ReactShadowNodeImpl.class.getSimpleName();
private static final YogaConfig sYogaConfig;
static {
sYogaConfig = ReactYogaConfigProvider.get();
sYogaConfig.setOnCloneNode(new YogaNodeCloneFunction() {
@Override
public YogaNode cloneNode(YogaNode oldYogaNode,
YogaNode parent,
int childIndex) {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"FabricReconciler.YogaNodeCloneFunction")
.flush();
try {
ReactShadowNodeImpl parentReactShadowNode = (ReactShadowNodeImpl) parent.getData();
Assertions.assertNotNull(parentReactShadowNode);
ReactShadowNodeImpl oldReactShadowNode = (ReactShadowNodeImpl) oldYogaNode.getData();
Assertions.assertNotNull(oldReactShadowNode);
if (DEBUG) {
FLog.d(
TAG,
"YogaNode started cloning: oldYogaNode: " + oldReactShadowNode + " - parent: "
+ parentReactShadowNode + " index: " + childIndex);
}
ReactShadowNodeImpl newNode = oldReactShadowNode.mutableCopy(oldReactShadowNode.getInstanceHandle());
parentReactShadowNode.replaceChild(newNode, childIndex);
return newNode.mYogaNode;
} finally{
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
});
}
private int mReactTag;
@ -168,94 +135,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
mIsSealed = false;
}
private void replaceChild(ReactShadowNodeImpl newNode, int childIndex) {
mChildren.remove(childIndex);
mChildren.add(childIndex, newNode);
newNode.mParent = this;
}
/**
* @return a copy of this object (no including copy of its children or the underlying yogaNode).
*/
protected ReactShadowNodeImpl copy() {
return new ReactShadowNodeImpl(this);
}
@Override
public ReactShadowNodeImpl mutableCopy(long instanceHandle) {
ReactShadowNodeImpl copy = copy();
Assertions.assertCondition(
getClass() == copy.getClass(),
"Copied shadow node must use the same class");
copy.mInstanceHandle = instanceHandle;
if (mYogaNode != null) {
copy.mYogaNode = mYogaNode.clone();
copy.mYogaNode.setData(copy);
} else {
// Virtual ReactShadowNode do not have a YogaNode associated
copy.mYogaNode = null;
}
copy.mTotalNativeChildren = mTotalNativeChildren;
copy.mNativeChildren = copyChildren(mNativeChildren);
copy.mChildren = copyChildren(mChildren);
return copy;
}
@Nullable
private ArrayList<ReactShadowNodeImpl> copyChildren(@Nullable List<ReactShadowNodeImpl> list){
ArrayList<ReactShadowNodeImpl> result = list == null ? null : new ArrayList<>(list);
if (result != null) {
for (ReactShadowNodeImpl child : result) {
child.mParent = null;
}
}
return result;
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewChildren(long instanceHandle) {
ReactShadowNodeImpl copy = copy();
copy.mInstanceHandle = instanceHandle;
Assertions.assertCondition(
getClass() == copy.getClass(),
"Copied shadow node must use the same class");
if (mYogaNode != null) {
copy.mYogaNode = mYogaNode.cloneWithNewChildren();
copy.mYogaNode.setData(copy);
} else {
// Virtual ReactShadowNode do not have a YogaNode associated
copy.mYogaNode = null;
}
copy.mNativeChildren = null;
copy.mChildren = null;
copy.mTotalNativeChildren = 0;
return copy;
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewProps(long instanceHandle,
@Nullable ReactStylesDiffMap newProps) {
ReactShadowNodeImpl copy = mutableCopy(instanceHandle);
if (newProps != null) {
copy.updateProperties(newProps);
copy.mNewProps = newProps;
}
return copy;
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewChildrenAndProps(long instanceHandle,
@Nullable ReactStylesDiffMap newProps) {
ReactShadowNodeImpl copy = mutableCopyWithNewChildren(instanceHandle);
if (newProps != null) {
copy.updateProperties(newProps);
copy.mNewProps = newProps;
}
return copy;
}
/**
* Nodes that return {@code true} will be treated as "virtual" nodes. That is, nodes that are not
* mapped into native views (e.g. nested text node). By default this method returns {@code false}.
@ -299,7 +178,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public final void markUpdateSeen() {
assertNotSealed();
mNodeUpdated = false;
if (hasNewLayout()) {
markLayoutSeen();
@ -325,7 +203,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void dirty() {
assertNotSealed();
if (!isVirtual()) {
mYogaNode.dirty();
}
@ -338,7 +215,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void addChildAt(ReactShadowNodeImpl child, int i) {
assertNotSealed();
if (mChildren == null) {
mChildren = new ArrayList<>(4);
}
@ -370,7 +246,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public ReactShadowNodeImpl removeChildAt(int i) {
assertNotSealed();
if (mChildren == null) {
throw new ArrayIndexOutOfBoundsException(
"Index " + i + " out of bounds: node has no children");
@ -543,7 +418,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setReactTag(int reactTag) {
assertNotSealed();
mReactTag = reactTag;
}
@ -555,13 +429,11 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public final void setRootTag(int rootTag) {
assertNotSealed();
mRootTag = rootTag;
}
@Override
public final void setViewClassName(String viewClassName) {
assertNotSealed();
mViewClassName = viewClassName;
}
@ -603,7 +475,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public final void markLayoutSeen() {
assertNotSealed();
if (mYogaNode != null) {
mYogaNode.markLayoutSeen();
}
@ -615,7 +486,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
*/
@Override
public final void addNativeChildAt(ReactShadowNodeImpl child, int nativeIndex) {
assertNotSealed();
Assertions.assertCondition(!mIsLayoutOnly);
Assertions.assertCondition(!child.mIsLayoutOnly);
@ -667,7 +537,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
*/
@Override
public final void setIsLayoutOnly(boolean isLayoutOnly) {
assertNotSealed();
Assertions.assertCondition(getParent() == null, "Must remove from no opt parent first");
Assertions.assertCondition(mNativeParent == null, "Must remove from native parent first");
Assertions.assertCondition(getNativeChildCount() == 0, "Must remove all native children first");
@ -816,7 +685,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setLayoutDirection(YogaDirection direction) {
assertNotSealed();
mYogaNode.setDirection(direction);
}
@ -827,43 +695,36 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setStyleWidth(float widthPx) {
assertNotSealed();
mYogaNode.setWidth(widthPx);
}
@Override
public void setStyleWidthPercent(float percent) {
assertNotSealed();
mYogaNode.setWidthPercent(percent);
}
@Override
public void setStyleWidthAuto() {
assertNotSealed();
mYogaNode.setWidthAuto();
}
@Override
public void setStyleMinWidth(float widthPx) {
assertNotSealed();
mYogaNode.setMinWidth(widthPx);
}
@Override
public void setStyleMinWidthPercent(float percent) {
assertNotSealed();
mYogaNode.setMinWidthPercent(percent);
}
@Override
public void setStyleMaxWidth(float widthPx) {
assertNotSealed();
mYogaNode.setMaxWidth(widthPx);
}
@Override
public void setStyleMaxWidthPercent(float percent) {
assertNotSealed();
mYogaNode.setMaxWidthPercent(percent);
}
@ -874,151 +735,126 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setStyleHeight(float heightPx) {
assertNotSealed();
mYogaNode.setHeight(heightPx);
}
@Override
public void setStyleHeightPercent(float percent) {
assertNotSealed();
mYogaNode.setHeightPercent(percent);
}
@Override
public void setStyleHeightAuto() {
assertNotSealed();
mYogaNode.setHeightAuto();
}
@Override
public void setStyleMinHeight(float widthPx) {
assertNotSealed();
mYogaNode.setMinHeight(widthPx);
}
@Override
public void setStyleMinHeightPercent(float percent) {
assertNotSealed();
mYogaNode.setMinHeightPercent(percent);
}
@Override
public void setStyleMaxHeight(float widthPx) {
assertNotSealed();
mYogaNode.setMaxHeight(widthPx);
}
@Override
public void setStyleMaxHeightPercent(float percent) {
assertNotSealed();
mYogaNode.setMaxHeightPercent(percent);
}
@Override
public void setFlex(float flex) {
assertNotSealed();
mYogaNode.setFlex(flex);
}
@Override
public void setFlexGrow(float flexGrow) {
assertNotSealed();
mYogaNode.setFlexGrow(flexGrow);
}
@Override
public void setFlexShrink(float flexShrink) {
assertNotSealed();
mYogaNode.setFlexShrink(flexShrink);
}
@Override
public void setFlexBasis(float flexBasis) {
assertNotSealed();
mYogaNode.setFlexBasis(flexBasis);
}
@Override
public void setFlexBasisAuto() {
assertNotSealed();
mYogaNode.setFlexBasisAuto();
}
@Override
public void setFlexBasisPercent(float percent) {
assertNotSealed();
mYogaNode.setFlexBasisPercent(percent);
}
@Override
public void setStyleAspectRatio(float aspectRatio) {
assertNotSealed();
mYogaNode.setAspectRatio(aspectRatio);
}
@Override
public void setFlexDirection(YogaFlexDirection flexDirection) {
assertNotSealed();
mYogaNode.setFlexDirection(flexDirection);
}
@Override
public void setFlexWrap(YogaWrap wrap) {
assertNotSealed();
mYogaNode.setWrap(wrap);
}
@Override
public void setAlignSelf(YogaAlign alignSelf) {
assertNotSealed();
mYogaNode.setAlignSelf(alignSelf);
}
@Override
public void setAlignItems(YogaAlign alignItems) {
assertNotSealed();
mYogaNode.setAlignItems(alignItems);
}
@Override
public void setAlignContent(YogaAlign alignContent) {
assertNotSealed();
mYogaNode.setAlignContent(alignContent);
}
@Override
public void setJustifyContent(YogaJustify justifyContent) {
assertNotSealed();
mYogaNode.setJustifyContent(justifyContent);
}
@Override
public void setOverflow(YogaOverflow overflow) {
assertNotSealed();
mYogaNode.setOverflow(overflow);
}
@Override
public void setDisplay(YogaDisplay display) {
assertNotSealed();
mYogaNode.setDisplay(display);
}
@Override
public void setMargin(int spacingType, float margin) {
assertNotSealed();
mYogaNode.setMargin(YogaEdge.fromInt(spacingType), margin);
}
@Override
public void setMarginPercent(int spacingType, float percent) {
assertNotSealed();
mYogaNode.setMarginPercent(YogaEdge.fromInt(spacingType), percent);
}
@Override
public void setMarginAuto(int spacingType) {
assertNotSealed();
mYogaNode.setMarginAuto(YogaEdge.fromInt(spacingType));
}
@ -1034,14 +870,12 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setDefaultPadding(int spacingType, float padding) {
assertNotSealed();
mDefaultPadding.set(spacingType, padding);
updatePadding();
}
@Override
public void setPadding(int spacingType, float padding) {
assertNotSealed();
mPadding[spacingType] = padding;
mPaddingIsPercent[spacingType] = false;
updatePadding();
@ -1049,14 +883,12 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setPaddingPercent(int spacingType, float percent) {
assertNotSealed();
mPadding[spacingType] = percent;
mPaddingIsPercent[spacingType] = !YogaConstants.isUndefined(percent);
updatePadding();
}
private void updatePadding() {
assertNotSealed();
for (int spacingType = Spacing.LEFT; spacingType <= Spacing.ALL; spacingType++) {
if (spacingType == Spacing.LEFT
|| spacingType == Spacing.RIGHT
@ -1092,43 +924,36 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
@Override
public void setBorder(int spacingType, float borderWidth) {
assertNotSealed();
mYogaNode.setBorder(YogaEdge.fromInt(spacingType), borderWidth);
}
@Override
public void setPosition(int spacingType, float position) {
assertNotSealed();
mYogaNode.setPosition(YogaEdge.fromInt(spacingType), position);
}
@Override
public void setPositionPercent(int spacingType, float percent) {
assertNotSealed();
mYogaNode.setPositionPercent(YogaEdge.fromInt(spacingType), percent);
}
@Override
public void setPositionType(YogaPositionType positionType) {
assertNotSealed();
mYogaNode.setPositionType(positionType);
}
@Override
public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
assertNotSealed();
mShouldNotifyOnLayout = shouldNotifyOnLayout;
}
@Override
public void setBaselineFunction(YogaBaselineFunction baselineFunction) {
assertNotSealed();
mYogaNode.setBaselineFunction(baselineFunction);
}
@Override
public void setMeasureFunction(YogaMeasureFunction measureFunction) {
assertNotSealed();
mYogaNode.setMeasureFunction(measureFunction);
}
@ -1184,43 +1009,6 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
return mChildren == null ? null : Collections.<ReactShadowNode>unmodifiableList(mChildren);
}
@Override
public ReactShadowNode getOriginalReactShadowNode() {
return mOriginalReactShadowNode;
}
@Override
public void setOriginalReactShadowNode(ReactShadowNode node) {
mOriginalReactShadowNode = node;
}
@Override
public long getInstanceHandle() {
return mInstanceHandle;
}
@Override
public void setInstanceHandle(long instanceHandle) {
assertNotSealed();
mInstanceHandle = instanceHandle;
}
@Override
public void markAsSealed() {
mIsSealed = true;
}
@Override
public boolean isSealed() {
return mIsSealed;
}
private void assertNotSealed() {
if (mIsSealed) {
throw new IllegalStateException("Can not modify sealed node " + toString());
}
}
@Override
public void updateScreenLayout(ReactShadowNode prevNode) {
mScreenHeight = prevNode.getScreenHeight();

View File

@ -28,11 +28,6 @@ public class ARTGroupShadowNode extends ARTVirtualNode {
public ARTGroupShadowNode() { }
public ARTGroupShadowNode(ARTGroupShadowNode node) {
super(node);
this.mClipping = new RectF(node.mClipping);
}
@ReactProp(name = "clipping")
public void setClipping(@Nullable ReadableArray clippingDims) {
float[] clippingData = PropHelper.toFloatArray(clippingDims);
@ -42,11 +37,6 @@ public class ARTGroupShadowNode extends ARTVirtualNode {
}
}
@Override
protected ReactShadowNodeImpl copy() {
return new ARTGroupShadowNode(this);
}
@Override
public boolean isVirtual() {
return true;

View File

@ -60,22 +60,6 @@ public class ARTShapeShadowNode extends ARTVirtualNode {
public ARTShapeShadowNode() { }
public ARTShapeShadowNode(ARTShapeShadowNode node) {
super(node);
mPath = new Path(node.mPath);
mStrokeColor = copyArray(node.mStrokeColor);
mBrushData = copyArray(node.mBrushData);
mStrokeDash = copyArray(node.mStrokeDash);
mStrokeWidth = node.mStrokeWidth;
mStrokeCap = node.mStrokeCap;
mStrokeJoin = node.mStrokeJoin;
}
@Override
protected ARTShapeShadowNode copy() {
return new ARTShapeShadowNode(this);
}
@ReactProp(name = "d")
public void setShapePath(@Nullable ReadableArray shapePath) {
float[] pathData = PropHelper.toFloatArray(shapePath);

View File

@ -42,17 +42,6 @@ public class ARTTextShadowNode extends ARTShapeShadowNode {
public ARTTextShadowNode() { }
public ARTTextShadowNode(ARTTextShadowNode node) {
super(node);
mTextAlignment = node.mTextAlignment;
mFrame = node.mFrame; // copy reference as mFrame is already immutable
}
@Override
protected ARTShapeShadowNode copy() {
return new ARTTextShadowNode(this);
}
@ReactProp(name = "frame")
public void setFrame(@Nullable ReadableMap frame) {
mFrame = frame;

View File

@ -37,13 +37,6 @@ public abstract class ARTVirtualNode extends ReactShadowNodeImpl {
mScale = DisplayMetricsHolder.getWindowDisplayMetrics().density;
}
protected ARTVirtualNode(ARTVirtualNode artVirtualNode) {
super(artVirtualNode);
mScale = artVirtualNode.mScale;
mOpacity = artVirtualNode.mOpacity;
mMatrix = new Matrix(artVirtualNode.mMatrix);
}
@Override
public boolean isVirtual() {
return true;

View File

@ -23,15 +23,6 @@ class ModalHostShadowNode extends LayoutShadowNode {
public ModalHostShadowNode() {}
private ModalHostShadowNode(ModalHostShadowNode node) {
super(node);
}
@Override
protected ModalHostShadowNode copy() {
return new ModalHostShadowNode(this);
}
/**
* We need to set the styleWidth and styleHeight of the one child (represented by the <View/>
* within the <RCTModalHostView/> in Modal.js. This needs to fill the entire window.

View File

@ -7,23 +7,19 @@
package com.facebook.react.views.progressbar;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Node responsible for holding the style of the ProgressBar, see under
@ -45,36 +41,10 @@ public class ProgressBarShadowNode extends LayoutShadowNode implements YogaMeasu
initMeasureFunction();
}
public ProgressBarShadowNode(ProgressBarShadowNode node) {
super(node);
mWidth = node.mWidth.clone();
mHeight = node.mHeight.clone();
mMeasured = new HashSet<>(node.mMeasured);
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewChildren(long instanceHandle) {
ProgressBarShadowNode node = (ProgressBarShadowNode) super.mutableCopyWithNewChildren(instanceHandle);
node.initMeasureFunction();
return node;
}
private void initMeasureFunction() {
setMeasureFunction(this);
}
@Override
public ReactShadowNodeImpl mutableCopy(long instanceHandle) {
ProgressBarShadowNode node = (ProgressBarShadowNode) super.mutableCopy(instanceHandle);
node.initMeasureFunction();
return node;
}
@Override
public ProgressBarShadowNode copy() {
return new ProgressBarShadowNode(this);
}
public @Nullable String getStyle() {
return mStyle;
}

View File

@ -51,36 +51,10 @@ public class ReactSliderManager extends SimpleViewManager<ReactSlider> {
initMeasureFunction();
}
private ReactSliderShadowNode(ReactSliderShadowNode node) {
super(node);
mWidth = node.mWidth;
mHeight = node.mHeight;
mMeasured = node.mMeasured;
}
private void initMeasureFunction() {
setMeasureFunction(this);
}
@Override
public ReactShadowNodeImpl mutableCopy(long instanceHandle) {
ReactSliderShadowNode reactShadowNode = (ReactSliderShadowNode) super.mutableCopy(instanceHandle);
reactShadowNode.initMeasureFunction();
return reactShadowNode;
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewChildren(long instanceHandle) {
ReactSliderShadowNode reactShadowNode = (ReactSliderShadowNode) super.mutableCopyWithNewChildren(instanceHandle);
reactShadowNode.initMeasureFunction();
return reactShadowNode;
}
@Override
protected ReactSliderShadowNode copy() {
return new ReactSliderShadowNode(this);
}
@Override
public long measure(
YogaNode node,

View File

@ -44,36 +44,10 @@ public class ReactSwitchManager extends SimpleViewManager<ReactSwitch> {
initMeasureFunction();
}
private ReactSwitchShadowNode(ReactSwitchShadowNode node) {
super(node);
mWidth = node.mWidth;
mHeight = node.mHeight;
mMeasured = node.mMeasured;
}
private void initMeasureFunction() {
setMeasureFunction(this);
}
@Override
public ReactShadowNodeImpl mutableCopy(long instanceHandle) {
ReactSwitchShadowNode reactShadowNode = (ReactSwitchShadowNode) super.mutableCopy(instanceHandle);
reactShadowNode.initMeasureFunction();
return reactShadowNode;
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewChildren(long instanceHandle) {
ReactSwitchShadowNode reactShadowNode = (ReactSwitchShadowNode) super.mutableCopyWithNewChildren(instanceHandle);
reactShadowNode.initMeasureFunction();
return reactShadowNode;
}
@Override
protected ReactSwitchShadowNode copy() {
return new ReactSwitchShadowNode(this);
}
@Override
public long measure(
YogaNode node,

View File

@ -302,38 +302,6 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
public ReactBaseTextShadowNode() {}
public ReactBaseTextShadowNode(ReactBaseTextShadowNode node) {
super(node);
mLineHeight = node.mLineHeight;
mIsColorSet = node.mIsColorSet;
mAllowFontScaling = node.mAllowFontScaling;
mColor = node.mColor;
mIsBackgroundColorSet = node.mIsBackgroundColorSet;
mBackgroundColor = node.mBackgroundColor;
mNumberOfLines = node.mNumberOfLines;
mFontSize = node.mFontSize;
mFontSizeInput = node.mFontSizeInput;
mLineHeightInput = node.mLineHeightInput;
mTextAlign = node.mTextAlign;
mTextBreakStrategy = node.mTextBreakStrategy;
mTextTransform = node.mTextTransform;
mTextShadowOffsetDx = node.mTextShadowOffsetDx;
mTextShadowOffsetDy = node.mTextShadowOffsetDy;
mTextShadowRadius = node.mTextShadowRadius;
mTextShadowColor = node.mTextShadowColor;
mIsUnderlineTextDecorationSet = node.mIsUnderlineTextDecorationSet;
mIsLineThroughTextDecorationSet = node.mIsLineThroughTextDecorationSet;
mIncludeFontPadding = node.mIncludeFontPadding;
mFontStyle = node.mFontStyle;
mFontWeight = node.mFontWeight;
mFontFamily = node.mFontFamily;
mContainsImages = node.mContainsImages;
mHeightOfTallestInlineImage = node.mHeightOfTallestInlineImage;
}
// Returns a line height which takes into account the requested line height
// and the height of the inline images.
public float getEffectiveLineHeight() {

View File

@ -24,16 +24,6 @@ public class ReactRawTextShadowNode extends ReactShadowNodeImpl {
public ReactRawTextShadowNode() { }
private ReactRawTextShadowNode(ReactRawTextShadowNode node) {
super(node);
this.mText = node.mText;
}
@Override
protected ReactShadowNodeImpl copy() {
return new ReactRawTextShadowNode(this);
}
@ReactProp(name = PROP_TEXT)
public void setText(@Nullable String text) {
mText = text;

View File

@ -23,8 +23,4 @@ public abstract class ReactTextInlineImageShadowNode extends LayoutShadowNode {
public ReactTextInlineImageShadowNode() {}
protected ReactTextInlineImageShadowNode(ReactTextInlineImageShadowNode node) {
super(node);
}
}

View File

@ -158,36 +158,12 @@ public class ReactTextShadowNode extends ReactBaseTextShadowNode {
initMeasureFunction();
}
private ReactTextShadowNode(ReactTextShadowNode node) {
super(node);
this.mPreparedSpannableText = node.mPreparedSpannableText;
}
private void initMeasureFunction() {
if (!isVirtual()) {
setMeasureFunction(mTextMeasureFunction);
}
}
@Override
protected LayoutShadowNode copy() {
return new ReactTextShadowNode(this);
}
@Override
public ReactShadowNodeImpl mutableCopy(long instanceHandle) {
ReactTextShadowNode copy = (ReactTextShadowNode) super.mutableCopy(instanceHandle);
copy.initMeasureFunction();
return copy;
}
@Override
public ReactShadowNodeImpl mutableCopyWithNewChildren(long instanceHandle) {
ReactTextShadowNode copy = (ReactTextShadowNode) super.mutableCopyWithNewChildren(instanceHandle);
copy.initMeasureFunction();
return copy;
}
// Return text alignment according to LTR or RTL style
private int getTextAlign() {
int textAlign = mTextAlign;

View File

@ -17,12 +17,4 @@ public class ReactVirtualTextShadowNode extends ReactBaseTextShadowNode {
public ReactVirtualTextShadowNode() { }
private ReactVirtualTextShadowNode(ReactVirtualTextShadowNode node) {
super(node);
}
@Override
protected ReactVirtualTextShadowNode copy() {
return new ReactVirtualTextShadowNode(this);
}
}

View File

@ -50,22 +50,6 @@ public class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineIm
mCallerContext = callerContext;
}
private FrescoBasedReactTextInlineImageShadowNode(FrescoBasedReactTextInlineImageShadowNode node) {
super(node);
mHeaders = node.mHeaders; // mHeaders is immutable
mWidth = node.mWidth;
mHeight = node.mHeight;
mTintColor = node.mTintColor;
mDraweeControllerBuilder = node.mDraweeControllerBuilder;
mCallerContext = node.mCallerContext;
mUri = node.mUri;
}
@Override
protected FrescoBasedReactTextInlineImageShadowNode copy() {
return new FrescoBasedReactTextInlineImageShadowNode(this);
}
@ReactProp(name = "src")
public void setSource(@Nullable ReadableArray sources) {
final String source =

View File

@ -54,44 +54,10 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
initMeasureFunction();
}
private ReactTextInputShadowNode(ReactTextInputShadowNode node) {
super(node);
mMostRecentEventCount = node.mMostRecentEventCount;
mText = node.mText;
mLocalData = node.mLocalData;
}
@Override
protected ReactTextInputShadowNode copy() {
return new ReactTextInputShadowNode(this);
}
@Override
public ReactTextInputShadowNode mutableCopy(long instanceHandle) {
ReactTextInputShadowNode node = (ReactTextInputShadowNode) super.mutableCopy(instanceHandle);
node.initMeasureFunction();
ThemedReactContext themedContext = getThemedContext();
if (themedContext != null) {
node.setThemedContext(themedContext);
}
return node;
}
private void initMeasureFunction() {
setMeasureFunction(this);
}
@Override
public ReactTextInputShadowNode mutableCopyWithNewChildren(long instanceHandle) {
ReactTextInputShadowNode node = (ReactTextInputShadowNode) super.mutableCopyWithNewChildren(instanceHandle);
node.initMeasureFunction();
ThemedReactContext themedContext = getThemedContext();
if (themedContext != null) {
node.setThemedContext(themedContext);
}
return node;
}
@Override
public void setThemedContext(ThemedReactContext themedContext) {
super.setThemedContext(themedContext);

View File

@ -1,40 +0,0 @@
load("//tools/build_defs/oss:rn_defs.bzl", "IS_OSS_BUILD", "react_native_dep", "react_native_target", "react_native_tests_target", "rn_robolectric_test")
rn_robolectric_test(
name = "fabric",
srcs = glob(["**/*.java"]),
contacts = ["oncall+fbandroid_sheriff@xmail.facebook.com"],
resources = glob([
"**/*.txt",
"**/*.json",
]),
visibility = [
"PUBLIC",
],
deps = [
"xplat//yoga/java:java",
react_native_dep("third-party/java/assertj:assertj-core"),
react_native_dep("third-party/java/fest:fest"),
react_native_dep("third-party/java/fest:fest_android"),
react_native_dep("third-party/java/guava:guava"),
react_native_dep("third-party/java/jackson:jackson"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_dep("third-party/java/jsr-330:jsr-330"),
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_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/android/support/v4:lib-support-v4"),
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"),
react_native_target("java/com/facebook/react/fabric:fabric"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/views/text:text"),
react_native_target("java/com/facebook/react/views/view:view"),
react_native_target("java/com/facebook/react/views/progressbar:progressbar"),
react_native_tests_target("java/com/facebook/react/bridge:testhelpers"),
],
) if not IS_OSS_BUILD else None

View File

@ -1,205 +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;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewAtIndex;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/** Tests {@link FabricReconciler} */
@RunWith(RobolectricTestRunner.class)
public class FabricReconcilerTest {
private FabricReconciler mFabricReconciler;
private FabricUIManager mFabricUIManager;
private MockUIViewOperationQueue mMockUIViewOperationQueue;
@Before
public void setUp() {
CatalystInstance catalystInstance = ReactTestHelper.createMockCatalystInstance();
ReactApplicationContext reactContext =
new ReactApplicationContext(RuntimeEnvironment.application);
reactContext.initializeWithInstance(catalystInstance);
List<ViewManager> viewManagers = new ArrayList<>();
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
JavaScriptContextHolder jsContext = mock(JavaScriptContextHolder.class);
EventDispatcher eventDispatcher = mock(EventDispatcher.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext, eventDispatcher);
mMockUIViewOperationQueue = new MockUIViewOperationQueue(reactContext);
mFabricReconciler = new FabricReconciler(mMockUIViewOperationQueue);
}
@Test
public void testSimpleHierarchy() {
ReactShadowNode parent = createNode(0);
ReactShadowNode child1 = createNode(1);
ReactShadowNode child2 = createNode(2);
addChildren(parent, child1, child2);
ReactShadowNode parentCloned = createNode(0);
ReactShadowNode child3 = createNode(3);
addChildren(parentCloned, child3, child2);
mFabricReconciler.manageChildren(parent, parentCloned);
List<ManageChildrenOperation> expectedOperations = new ArrayList<>();
expectedOperations.add(
new ManageChildrenOperation(
0,
new int[] {0, 1},
new ViewAtIndex[] {new ViewAtIndex(3, 0), new ViewAtIndex(2, 1)},
new int[] {1}));
assertThat(mMockUIViewOperationQueue.getOperations()).isEqualTo(expectedOperations);
}
@Test
public void testVirtualNodes() {
ReactShadowNode parent = createNode(0);
ReactShadowNode child1 = createVirtualNode(1);
ReactShadowNode child2 = createVirtualNode(2);
ReactShadowNode child3 = createVirtualNode(3);
addChildren(parent, child1, child2, child3);
ReactShadowNode parentCloned = createNode(0);
ReactShadowNode child4 = createVirtualNode(4);
addChildren(parentCloned, child1, child4, child3);
mFabricReconciler.manageChildren(parent, parentCloned);
List<ManageChildrenOperation> expectedOperations = new ArrayList<>();
assertThat(mMockUIViewOperationQueue.getOperations()).isEqualTo(expectedOperations);
}
private void addChildren(ReactShadowNode parent, ReactShadowNode... children) {
for (ReactShadowNode child : children) {
mFabricUIManager.appendChild(parent, child);
}
}
private static ReactShadowNode createNode(int tag) {
return createNode(tag, false);
}
private static ReactShadowNode createVirtualNode(int tag) {
return createNode(tag, true);
}
private static ReactShadowNode createNode(int tag, boolean virtual) {
ReactShadowNode node;
if (virtual) {
node = new VirtualReactShadowNode();
} else {
node = new ReactShadowNodeImpl();
}
node.setReactTag(tag);
node.setViewClassName("View");
node.setThemedContext(mock(ThemedReactContext.class));
return node;
}
private static class VirtualReactShadowNode extends ReactShadowNodeImpl {
VirtualReactShadowNode() {}
VirtualReactShadowNode(VirtualReactShadowNode original) {
super(original);
}
@Override
public boolean isVirtual() {
return true;
}
@Override
public ReactShadowNodeImpl copy() {
return new VirtualReactShadowNode(this);
}
}
private static class ManageChildrenOperation {
private int mTag;
private int[] mIndicesToRemove;
private ViewAtIndex[] mViewsToAdd;
private int[] mTagsToRemove;
private ManageChildrenOperation(
int tag, int[] indicesToRemove, ViewAtIndex[] viewsToAdd, int[] tagsToRemove) {
mTag = tag;
mIndicesToRemove = indicesToRemove;
mViewsToAdd = viewsToAdd;
mTagsToRemove = tagsToRemove;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
ManageChildrenOperation op = (ManageChildrenOperation) obj;
return mTag == op.mTag
&& Arrays.equals(mIndicesToRemove, op.mIndicesToRemove)
&& Arrays.equals(mViewsToAdd, op.mViewsToAdd)
&& Arrays.equals(mTagsToRemove, op.mTagsToRemove);
}
@Override
public int hashCode() {
return Arrays.deepHashCode(new Object[] {mTag, mIndicesToRemove, mViewsToAdd, mTagsToRemove});
}
@Override
public String toString() {
return "ManageChildrenOperation \n\tindicesToRemove: "
+ Arrays.toString(mIndicesToRemove)
+ "\n\tviewsToAdd: "
+ Arrays.toString(mViewsToAdd)
+ "\n\ttagsToRemove: "
+ Arrays.toString(mTagsToRemove);
}
}
private static class MockUIViewOperationQueue extends UIViewOperationQueue {
private List<ManageChildrenOperation> mOperations;
private MockUIViewOperationQueue(ReactApplicationContext context) {
super(context, mock(NativeViewHierarchyManager.class), 0);
mOperations = new ArrayList<>();
}
@Override
public void enqueueManageChildren(
int reactTag, int[] indicesToRemove, ViewAtIndex[] viewsToAdd, int[] tagsToDelete) {
mOperations.add(
new ManageChildrenOperation(reactTag, indicesToRemove, viewsToAdd, tagsToDelete));
}
public List<ManageChildrenOperation> getOperations() {
return Collections.unmodifiableList(mOperations);
}
}
}

View File

@ -1,336 +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;
import static org.fest.assertions.api.Assertions.assertThat;
import static com.facebook.react.bridge.InstanceHandleHelper.randomInstanceHandle;
import static org.mockito.Mockito.mock;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.react.views.progressbar.ProgressBarShadowNode;
import com.facebook.react.views.text.ReactRawTextManager;
import com.facebook.react.views.text.ReactRawTextShadowNode;
import com.facebook.react.views.text.ReactTextViewManager;
import com.facebook.react.views.view.ReactViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.fest.assertions.data.Offset;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/** Tests {@link FabricUIManager} */
@RunWith(RobolectricTestRunner.class)
public class FabricUIManagerTest {
private FabricUIManager mFabricUIManager;
private ThemedReactContext mThemedReactContext;
private int mNextReactTag;
@Before
public void setUp() throws Exception {
mNextReactTag = 2;
ReactApplicationContext reactContext = new ReactApplicationContext(RuntimeEnvironment.application);
reactContext.initializeWithInstance(ReactTestHelper.createMockCatalystInstance());
mThemedReactContext = new ThemedReactContext(reactContext, reactContext);
List<ViewManager> viewManagers =
Arrays.<ViewManager>asList(
new ReactViewManager(), new ReactTextViewManager(), new ReactRawTextManager());
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
JavaScriptContextHolder jsContext = mock(JavaScriptContextHolder.class);
EventDispatcher eventDispatcher = mock(EventDispatcher.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext, eventDispatcher);
}
@Test
public void testCreateNode() {
ReactRootView rootView =
new ReactRootView(RuntimeEnvironment.application.getApplicationContext());
int rootTag = mFabricUIManager.addRootView(rootView);
int reactTag = mNextReactTag++;
String viewClass = ReactViewManager.REACT_CLASS;
ReactShadowNode node =
mFabricUIManager.createNode(reactTag, viewClass, rootTag, null, randomInstanceHandle());
assertThat(reactTag).isEqualTo(node.getReactTag());
assertThat(viewClass).isEqualTo(node.getViewClass());
assertThat(rootTag).isEqualTo(rootTag);
}
@Test
public void testCreateMultpleRootViews() {
createAndRenderRootView();
createAndRenderRootView();
}
private int createAndRenderRootView() {
ReactRootView rootView =
new ReactRootView(RuntimeEnvironment.application.getApplicationContext());
int rootTag = mFabricUIManager.addRootView(rootView);
int reactTag = mNextReactTag++;
String viewClass = ReactViewManager.REACT_CLASS;
ReactShadowNode node =
mFabricUIManager.createNode(reactTag, viewClass, rootTag, null, randomInstanceHandle());
List<ReactShadowNode> childSet = mFabricUIManager.createChildSet(rootTag);
mFabricUIManager.appendChildToSet(childSet, node);
mFabricUIManager.completeRoot(rootTag, childSet);
return rootTag;
}
@Test
public void testCloneNode() {
ReactShadowNode node = createViewNode();
ReactShadowNode child = createViewNode();
node.addChildAt(child, 0);
ReactShadowNode clonedNode = mFabricUIManager.cloneNode(node);
assertThat(clonedNode).isNotSameAs(node);
assertThat(clonedNode.getOriginalReactShadowNode()).isSameAs(node);
assertSameFields(clonedNode, node);
assertSameChildren(clonedNode, node);
assertThat(clonedNode.getChildAt(0)).isEqualTo(child);
}
@Test
public void testDefaultSpacingCloning() {
ReactShadowNode node = createViewNode();
node.setDefaultPadding(Spacing.LEFT, 10);
ReactShadowNode clonedNode = mFabricUIManager.cloneNode(node);
node.setDefaultPadding(Spacing.LEFT, 20);
assertThat(clonedNode.getStylePadding(Spacing.LEFT).value).isEqualTo(10f, Offset.offset(0.01f));
assertThat(node.getStylePadding(Spacing.LEFT).value).isEqualTo(20f, Offset.offset(0.01f));
}
@Test
public void testCloneVirtualNode() {
ReactRawTextShadowNode node = new ReactRawTextShadowNode();
node.setText("test");
assertThat(node.isVirtual()).isTrue();
ReactRawTextShadowNode clonedNode = (ReactRawTextShadowNode) node.mutableCopy(randomInstanceHandle());
assertThat(clonedNode.getText()).isEqualTo("test");
assertThat(clonedNode).isNotEqualTo(node);
}
@Test
public void testLayoutProgressBarAfterClonning() {
ProgressBarShadowNode node = new ProgressBarShadowNode();
node.setThemedContext(mThemedReactContext);
ProgressBarShadowNode clone = (ProgressBarShadowNode) node.mutableCopy(randomInstanceHandle());
clone.calculateLayout();
}
@Test
public void testCloneNodeWithNewChildren() {
ReactShadowNode node = createViewNode();
ReactShadowNode child = createViewNode();
node.addChildAt(child, 0);
ReactShadowNode clonedNode = mFabricUIManager.cloneNodeWithNewChildren(node);
assertThat(clonedNode.getChildCount()).isZero();
assertSameFields(clonedNode, node);
}
@Test
public void testCloneNodeWithNewProps() {
ReactShadowNode node = createViewNode();
ReadableNativeMap props = null; // TODO(ayc): Figure out how to create a Native map from tests.
ReactShadowNode clonedNode = mFabricUIManager.cloneNodeWithNewProps(node, props);
}
@Test
public void testCloneNodeWithNewChildrenAndProps() {
ReactShadowNode node = createViewNode();
ReadableNativeMap props = null;
ReactShadowNode clonedNode = mFabricUIManager.cloneNodeWithNewChildrenAndProps(node, props);
assertThat(clonedNode.getChildCount()).isZero();
}
@Test
public void testAppendChild() {
ReactShadowNode node = createViewNode();
ReactShadowNode child = createViewNode();
mFabricUIManager.appendChild(node, child);
assertThat(node.getChildCount()).isEqualTo(1);
assertThat(node.getChildAt(0)).isEqualTo(child);
}
@Test
public void testCreateChildSet() {
List<ReactShadowNode> childSet = mFabricUIManager.createChildSet(0);
assertThat(childSet).isEmpty();
}
@Test
public void testAppendChildToSet() {
ReactShadowNode node = createViewNode();
List<ReactShadowNode> childSet = mFabricUIManager.createChildSet(0);
mFabricUIManager.appendChildToSet(childSet, node);
assertThat(childSet).hasSize(1);
assertThat(childSet).contains(node);
}
@Test(expected = AssertionError.class)
public void testCompleteRootBeforeAddRoot() {
mFabricUIManager.completeRoot(0, new ArrayList<ReactShadowNode>());
}
@Test
public void testCompleteRoot() {
ReactRootView rootView =
new ReactRootView(RuntimeEnvironment.application.getApplicationContext());
int rootTag = mFabricUIManager.addRootView(rootView);
List<ReactShadowNode> children = mFabricUIManager.createChildSet(rootTag);
mFabricUIManager.completeRoot(rootTag, children);
}
@Test
public void testSealReactShadowNode() {
ReactRootView rootView =
new ReactRootView(RuntimeEnvironment.application.getApplicationContext());
int rootTag = mFabricUIManager.addRootView(rootView);
String viewClass = ReactViewManager.REACT_CLASS;
ReactShadowNode container = mFabricUIManager.createNode(6, viewClass, rootTag, null, randomInstanceHandle());
List<ReactShadowNode> childSet = mFabricUIManager.createChildSet(rootTag);
mFabricUIManager.appendChildToSet(childSet, container);
assertThat(container.isSealed()).isFalse();
mFabricUIManager.completeRoot(rootTag, childSet);
assertThat(container.isSealed()).isTrue();
}
/**
* Tests that cloned text nodes will not share measure functions
*/
@Test
public void testTextMutableClone() {
ReactRootView rootView =
new ReactRootView(RuntimeEnvironment.application.getApplicationContext());
int rootTag = mFabricUIManager.addRootView(rootView);
ReactShadowNode text =
mFabricUIManager.createNode(0, ReactTextViewManager.REACT_CLASS, rootTag, null, randomInstanceHandle());
assertThat(text.isMeasureDefined()).isTrue();
ReactShadowNode textCopy = text.mutableCopy(randomInstanceHandle());
assertThat(textCopy.isMeasureDefined()).isTrue();
textCopy.setStyleWidth(200);
text.onBeforeLayout();
text.calculateLayout();
textCopy.onBeforeLayout();
textCopy.calculateLayout();
assertThat(text.getLayoutWidth()).isNotEqualTo(textCopy.getLayoutWidth());
}
/**
* Verifies that the reconciliation phase will always set the originalNode field of every node in
* the tree to null once completeRoot has finished to prevent memory leaks.
*/
@Test
public void testRemoveOriginalNodeReferences() {
ReactRootView rootView =
new ReactRootView(RuntimeEnvironment.application.getApplicationContext());
int rootTag = mFabricUIManager.addRootView(rootView);
String viewClass = ReactViewManager.REACT_CLASS;
ReactShadowNode aa = mFabricUIManager.createNode(2, viewClass, rootTag, null, randomInstanceHandle());
ReactShadowNode a = mFabricUIManager.createNode(3, viewClass, rootTag, null, randomInstanceHandle());
mFabricUIManager.appendChild(a, aa);
ReactShadowNode bb = mFabricUIManager.createNode(4, viewClass, rootTag, null, randomInstanceHandle());
ReactShadowNode b = mFabricUIManager.createNode(5, viewClass, rootTag, null, randomInstanceHandle());
mFabricUIManager.appendChild(b, bb);
ReactShadowNode container = mFabricUIManager.createNode(6, viewClass, rootTag, null, randomInstanceHandle());
mFabricUIManager.appendChild(container, a);
mFabricUIManager.appendChild(container, b);
List<ReactShadowNode> childSet = mFabricUIManager.createChildSet(rootTag);
mFabricUIManager.appendChildToSet(childSet, container);
mFabricUIManager.completeRoot(rootTag, childSet);
ReactShadowNode aaClone = mFabricUIManager.cloneNodeWithNewProps(aa, null);
ReactShadowNode aClone = mFabricUIManager.cloneNodeWithNewChildren(a);
mFabricUIManager.appendChild(aClone, aaClone);
ReactShadowNode containerClone = mFabricUIManager.cloneNodeWithNewChildren(container);
mFabricUIManager.appendChild(containerClone, b);
mFabricUIManager.appendChild(containerClone, aClone);
List<ReactShadowNode> childSet2 = mFabricUIManager.createChildSet(rootTag);
mFabricUIManager.appendChildToSet(childSet2, containerClone);
mFabricUIManager.completeRoot(rootTag, childSet2);
ReactShadowNode[] nodes =
new ReactShadowNode[] {aa, a, bb, b, container, aaClone, aClone, containerClone};
for (ReactShadowNode node : nodes) {
assertThat(node.getOriginalReactShadowNode()).isNull();
}
}
private void assertSameChildren(ReactShadowNode node1, ReactShadowNode node2) {
assertThat(node1.getChildCount()).isEqualTo(node2.getChildCount());
for (int i = 0; i < node1.getChildCount(); i++) {
assertThat(node1.getChildAt(i)).isEqualTo(node2.getChildAt(i));
}
}
private void assertSameFields(ReactShadowNode node1, ReactShadowNode node2) {
assertThat(node1.getReactTag()).isEqualTo(node2.getReactTag());
assertThat(node1.getViewClass()).isEqualTo(node2.getViewClass());
assertThat(node2.getParent()).isNull();
assertThat(node1.getThemedContext()).isEqualTo(node2.getThemedContext());
assertThat(node1.isVirtual()).isEqualTo(node2.isVirtual());
assertThat(node1.getLayoutDirection()).isEqualTo(node2.getLayoutDirection());
assertThat(node1.getLayoutHeight()).isEqualTo(node2.getLayoutHeight());
assertThat(node1.getLayoutWidth()).isEqualTo(node2.getLayoutWidth());
assertThat(node1.getLayoutX()).isEqualTo(node2.getLayoutX());
assertThat(node1.getLayoutY()).isEqualTo(node2.getLayoutY());
for (int spacingType = Spacing.LEFT; spacingType <= Spacing.ALL; spacingType++) {
assertThat(node1.getStylePadding(spacingType)).isEqualTo(node2.getStylePadding(spacingType));
}
assertThat(node1.getStyleWidth()).isEqualTo(node2.getStyleWidth());
assertThat(node1.getStyleHeight()).isEqualTo(node2.getStyleHeight());
}
private ReactShadowNode createViewNode() {
ReactShadowNode node = new ReactShadowNodeImpl();
node.setViewClassName(ReactViewManager.REACT_CLASS);
node.setThemedContext(mThemedReactContext);
return node;
}
}

View File

@ -1,23 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
package com.facebook.react.fabric;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Tests {@link ReactShadowNode} */
@RunWith(RobolectricTestRunner.class)
public class ReactShadowNodeTest {
@Test(expected = AssertionError.class)
public void testClonedInstance() {
TestReactShadowNode node = new TestReactShadowNode();
node.mutableCopy(node.getInstanceHandle());
}
private static class TestReactShadowNode extends ReactShadowNodeImpl {}
}