mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 22:58:19 +00:00
Enforced thread safety on UIImplementation methods that mutate the shadowNodeRegistry
This commit is contained in:
parent
54af5b151e
commit
f5a31801a0
@ -43,6 +43,7 @@ import javax.annotation.Nullable;
|
|||||||
* shadow node hierarchy that is then mapped to a native view hierarchy.
|
* shadow node hierarchy that is then mapped to a native view hierarchy.
|
||||||
*/
|
*/
|
||||||
public class UIImplementation {
|
public class UIImplementation {
|
||||||
|
protected Object uiImplementationThreadLock = new Object();
|
||||||
|
|
||||||
protected final EventDispatcher mEventDispatcher;
|
protected final EventDispatcher mEventDispatcher;
|
||||||
protected final ReactApplicationContext mReactContext;
|
protected final ReactApplicationContext mReactContext;
|
||||||
@ -197,23 +198,25 @@ public class UIImplementation {
|
|||||||
*/
|
*/
|
||||||
public <T extends SizeMonitoringFrameLayout & MeasureSpecProvider> void registerRootView(
|
public <T extends SizeMonitoringFrameLayout & MeasureSpecProvider> void registerRootView(
|
||||||
T rootView, int tag, ThemedReactContext context) {
|
T rootView, int tag, ThemedReactContext context) {
|
||||||
final ReactShadowNode rootCSSNode = createRootShadowNode();
|
synchronized (uiImplementationThreadLock) {
|
||||||
rootCSSNode.setReactTag(tag);
|
final ReactShadowNode rootCSSNode = createRootShadowNode();
|
||||||
rootCSSNode.setThemedContext(context);
|
rootCSSNode.setReactTag(tag);
|
||||||
|
rootCSSNode.setThemedContext(context);
|
||||||
|
|
||||||
int widthMeasureSpec = rootView.getWidthMeasureSpec();
|
int widthMeasureSpec = rootView.getWidthMeasureSpec();
|
||||||
int heightMeasureSpec = rootView.getHeightMeasureSpec();
|
int heightMeasureSpec = rootView.getHeightMeasureSpec();
|
||||||
updateRootView(rootCSSNode, widthMeasureSpec, heightMeasureSpec);
|
updateRootView(rootCSSNode, widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
context.runOnNativeModulesQueueThread(new Runnable() {
|
context.runOnNativeModulesQueueThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mShadowNodeRegistry.addRootNode(rootCSSNode);
|
mShadowNodeRegistry.addRootNode(rootCSSNode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// register it within NativeViewHierarchyManager
|
// register it within NativeViewHierarchyManager
|
||||||
mOperationsQueue.addRootView(tag, rootView, context);
|
mOperationsQueue.addRootView(tag, rootView, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,7 +231,9 @@ public class UIImplementation {
|
|||||||
* Unregisters a root node with a given tag from the shadow node registry
|
* Unregisters a root node with a given tag from the shadow node registry
|
||||||
*/
|
*/
|
||||||
public void removeRootShadowNode(int rootViewTag) {
|
public void removeRootShadowNode(int rootViewTag) {
|
||||||
mShadowNodeRegistry.removeRootNode(rootViewTag);
|
synchronized (uiImplementationThreadLock) {
|
||||||
|
mShadowNodeRegistry.removeRootNode(rootViewTag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -279,23 +284,25 @@ public class UIImplementation {
|
|||||||
* Invoked by React to create a new node with a given tag, class name and properties.
|
* Invoked by React to create a new node with a given tag, class name and properties.
|
||||||
*/
|
*/
|
||||||
public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
|
public void createView(int tag, String className, int rootViewTag, ReadableMap props) {
|
||||||
ReactShadowNode cssNode = createShadowNode(className);
|
synchronized (uiImplementationThreadLock) {
|
||||||
ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag);
|
ReactShadowNode cssNode = createShadowNode(className);
|
||||||
Assertions.assertNotNull(rootNode, "Root node with tag " + rootViewTag + " doesn't exist");
|
ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag);
|
||||||
cssNode.setReactTag(tag);
|
Assertions.assertNotNull(rootNode, "Root node with tag " + rootViewTag + " doesn't exist");
|
||||||
cssNode.setViewClassName(className);
|
cssNode.setReactTag(tag);
|
||||||
cssNode.setRootTag(rootNode.getReactTag());
|
cssNode.setViewClassName(className);
|
||||||
cssNode.setThemedContext(rootNode.getThemedContext());
|
cssNode.setRootTag(rootNode.getReactTag());
|
||||||
|
cssNode.setThemedContext(rootNode.getThemedContext());
|
||||||
|
|
||||||
mShadowNodeRegistry.addNode(cssNode);
|
mShadowNodeRegistry.addNode(cssNode);
|
||||||
|
|
||||||
ReactStylesDiffMap styles = null;
|
ReactStylesDiffMap styles = null;
|
||||||
if (props != null) {
|
if (props != null) {
|
||||||
styles = new ReactStylesDiffMap(props);
|
styles = new ReactStylesDiffMap(props);
|
||||||
cssNode.updateProperties(styles);
|
cssNode.updateProperties(styles);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCreateView(cssNode, rootViewTag, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreateView(cssNode, rootViewTag, styles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleCreateView(
|
protected void handleCreateView(
|
||||||
@ -363,106 +370,108 @@ public class UIImplementation {
|
|||||||
@Nullable ReadableArray addChildTags,
|
@Nullable ReadableArray addChildTags,
|
||||||
@Nullable ReadableArray addAtIndices,
|
@Nullable ReadableArray addAtIndices,
|
||||||
@Nullable ReadableArray removeFrom) {
|
@Nullable ReadableArray removeFrom) {
|
||||||
ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag);
|
synchronized (uiImplementationThreadLock) {
|
||||||
|
ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag);
|
||||||
|
|
||||||
int numToMove = moveFrom == null ? 0 : moveFrom.size();
|
int numToMove = moveFrom == null ? 0 : moveFrom.size();
|
||||||
int numToAdd = addChildTags == null ? 0 : addChildTags.size();
|
int numToAdd = addChildTags == null ? 0 : addChildTags.size();
|
||||||
int numToRemove = removeFrom == null ? 0 : removeFrom.size();
|
int numToRemove = removeFrom == null ? 0 : removeFrom.size();
|
||||||
|
|
||||||
if (numToMove != 0 && (moveTo == null || numToMove != moveTo.size())) {
|
if (numToMove != 0 && (moveTo == null || numToMove != moveTo.size())) {
|
||||||
throw new IllegalViewOperationException("Size of moveFrom != size of moveTo!");
|
throw new IllegalViewOperationException("Size of moveFrom != size of moveTo!");
|
||||||
}
|
|
||||||
|
|
||||||
if (numToAdd != 0 && (addAtIndices == null || numToAdd != addAtIndices.size())) {
|
|
||||||
throw new IllegalViewOperationException("Size of addChildTags != size of addAtIndices!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We treat moves as an add and a delete
|
|
||||||
ViewAtIndex[] viewsToAdd = new ViewAtIndex[numToMove + numToAdd];
|
|
||||||
int[] indicesToRemove = new int[numToMove + numToRemove];
|
|
||||||
int[] tagsToRemove = new int[indicesToRemove.length];
|
|
||||||
int[] tagsToDelete = new int[numToRemove];
|
|
||||||
|
|
||||||
if (numToMove > 0) {
|
|
||||||
Assertions.assertNotNull(moveFrom);
|
|
||||||
Assertions.assertNotNull(moveTo);
|
|
||||||
for (int i = 0; i < numToMove; i++) {
|
|
||||||
int moveFromIndex = moveFrom.getInt(i);
|
|
||||||
int tagToMove = cssNodeToManage.getChildAt(moveFromIndex).getReactTag();
|
|
||||||
viewsToAdd[i] = new ViewAtIndex(
|
|
||||||
tagToMove,
|
|
||||||
moveTo.getInt(i));
|
|
||||||
indicesToRemove[i] = moveFromIndex;
|
|
||||||
tagsToRemove[i] = tagToMove;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (numToAdd > 0) {
|
if (numToAdd != 0 && (addAtIndices == null || numToAdd != addAtIndices.size())) {
|
||||||
Assertions.assertNotNull(addChildTags);
|
throw new IllegalViewOperationException("Size of addChildTags != size of addAtIndices!");
|
||||||
Assertions.assertNotNull(addAtIndices);
|
|
||||||
for (int i = 0; i < numToAdd; i++) {
|
|
||||||
int viewTagToAdd = addChildTags.getInt(i);
|
|
||||||
int indexToAddAt = addAtIndices.getInt(i);
|
|
||||||
viewsToAdd[numToMove + i] = new ViewAtIndex(viewTagToAdd, indexToAddAt);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (numToRemove > 0) {
|
// We treat moves as an add and a delete
|
||||||
Assertions.assertNotNull(removeFrom);
|
ViewAtIndex[] viewsToAdd = new ViewAtIndex[numToMove + numToAdd];
|
||||||
for (int i = 0; i < numToRemove; i++) {
|
int[] indicesToRemove = new int[numToMove + numToRemove];
|
||||||
int indexToRemove = removeFrom.getInt(i);
|
int[] tagsToRemove = new int[indicesToRemove.length];
|
||||||
int tagToRemove = cssNodeToManage.getChildAt(indexToRemove).getReactTag();
|
int[] tagsToDelete = new int[numToRemove];
|
||||||
indicesToRemove[numToMove + i] = indexToRemove;
|
|
||||||
tagsToRemove[numToMove + i] = tagToRemove;
|
if (numToMove > 0) {
|
||||||
tagsToDelete[i] = tagToRemove;
|
Assertions.assertNotNull(moveFrom);
|
||||||
|
Assertions.assertNotNull(moveTo);
|
||||||
|
for (int i = 0; i < numToMove; i++) {
|
||||||
|
int moveFromIndex = moveFrom.getInt(i);
|
||||||
|
int tagToMove = cssNodeToManage.getChildAt(moveFromIndex).getReactTag();
|
||||||
|
viewsToAdd[i] = new ViewAtIndex(
|
||||||
|
tagToMove,
|
||||||
|
moveTo.getInt(i));
|
||||||
|
indicesToRemove[i] = moveFromIndex;
|
||||||
|
tagsToRemove[i] = tagToMove;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// NB: moveFrom and removeFrom are both relative to the starting state of the View's children.
|
if (numToAdd > 0) {
|
||||||
// moveTo and addAt are both relative to the final state of the View's children.
|
Assertions.assertNotNull(addChildTags);
|
||||||
//
|
Assertions.assertNotNull(addAtIndices);
|
||||||
// 1) Sort the views to add and indices to remove by index
|
for (int i = 0; i < numToAdd; i++) {
|
||||||
// 2) Iterate the indices being removed from high to low and remove them. Going high to low
|
int viewTagToAdd = addChildTags.getInt(i);
|
||||||
// makes sure we remove the correct index when there are multiple to remove.
|
int indexToAddAt = addAtIndices.getInt(i);
|
||||||
// 3) Iterate the views being added by index low to high and add them. Like the view removal,
|
viewsToAdd[numToMove + i] = new ViewAtIndex(viewTagToAdd, indexToAddAt);
|
||||||
// iteration direction is important to preserve the correct index.
|
}
|
||||||
|
|
||||||
Arrays.sort(viewsToAdd, ViewAtIndex.COMPARATOR);
|
|
||||||
Arrays.sort(indicesToRemove);
|
|
||||||
|
|
||||||
// Apply changes to CSSNodeDEPRECATED hierarchy
|
|
||||||
int lastIndexRemoved = -1;
|
|
||||||
for (int i = indicesToRemove.length - 1; i >= 0; i--) {
|
|
||||||
int indexToRemove = indicesToRemove[i];
|
|
||||||
if (indexToRemove == lastIndexRemoved) {
|
|
||||||
throw new IllegalViewOperationException("Repeated indices in Removal list for view tag: "
|
|
||||||
+ viewTag);
|
|
||||||
}
|
}
|
||||||
cssNodeToManage.removeChildAt(indicesToRemove[i]);
|
|
||||||
lastIndexRemoved = indicesToRemove[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < viewsToAdd.length; i++) {
|
if (numToRemove > 0) {
|
||||||
ViewAtIndex viewAtIndex = viewsToAdd[i];
|
Assertions.assertNotNull(removeFrom);
|
||||||
ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(viewAtIndex.mTag);
|
for (int i = 0; i < numToRemove; i++) {
|
||||||
if (cssNodeToAdd == null) {
|
int indexToRemove = removeFrom.getInt(i);
|
||||||
throw new IllegalViewOperationException("Trying to add unknown view tag: "
|
int tagToRemove = cssNodeToManage.getChildAt(indexToRemove).getReactTag();
|
||||||
+ viewAtIndex.mTag);
|
indicesToRemove[numToMove + i] = indexToRemove;
|
||||||
|
tagsToRemove[numToMove + i] = tagToRemove;
|
||||||
|
tagsToDelete[i] = tagToRemove;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cssNodeToManage.addChildAt(cssNodeToAdd, viewAtIndex.mIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cssNodeToManage.isVirtual() && !cssNodeToManage.isVirtualAnchor()) {
|
// NB: moveFrom and removeFrom are both relative to the starting state of the View's children.
|
||||||
mNativeViewHierarchyOptimizer.handleManageChildren(
|
// moveTo and addAt are both relative to the final state of the View's children.
|
||||||
cssNodeToManage,
|
//
|
||||||
indicesToRemove,
|
// 1) Sort the views to add and indices to remove by index
|
||||||
tagsToRemove,
|
// 2) Iterate the indices being removed from high to low and remove them. Going high to low
|
||||||
viewsToAdd,
|
// makes sure we remove the correct index when there are multiple to remove.
|
||||||
tagsToDelete);
|
// 3) Iterate the views being added by index low to high and add them. Like the view removal,
|
||||||
}
|
// iteration direction is important to preserve the correct index.
|
||||||
|
|
||||||
for (int i = 0; i < tagsToDelete.length; i++) {
|
Arrays.sort(viewsToAdd, ViewAtIndex.COMPARATOR);
|
||||||
removeShadowNode(mShadowNodeRegistry.getNode(tagsToDelete[i]));
|
Arrays.sort(indicesToRemove);
|
||||||
|
|
||||||
|
// Apply changes to CSSNodeDEPRECATED hierarchy
|
||||||
|
int lastIndexRemoved = -1;
|
||||||
|
for (int i = indicesToRemove.length - 1; i >= 0; i--) {
|
||||||
|
int indexToRemove = indicesToRemove[i];
|
||||||
|
if (indexToRemove == lastIndexRemoved) {
|
||||||
|
throw new IllegalViewOperationException("Repeated indices in Removal list for view tag: "
|
||||||
|
+ viewTag);
|
||||||
|
}
|
||||||
|
cssNodeToManage.removeChildAt(indicesToRemove[i]);
|
||||||
|
lastIndexRemoved = indicesToRemove[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < viewsToAdd.length; i++) {
|
||||||
|
ViewAtIndex viewAtIndex = viewsToAdd[i];
|
||||||
|
ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(viewAtIndex.mTag);
|
||||||
|
if (cssNodeToAdd == null) {
|
||||||
|
throw new IllegalViewOperationException("Trying to add unknown view tag: "
|
||||||
|
+ viewAtIndex.mTag);
|
||||||
|
}
|
||||||
|
cssNodeToManage.addChildAt(cssNodeToAdd, viewAtIndex.mIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cssNodeToManage.isVirtual() && !cssNodeToManage.isVirtualAnchor()) {
|
||||||
|
mNativeViewHierarchyOptimizer.handleManageChildren(
|
||||||
|
cssNodeToManage,
|
||||||
|
indicesToRemove,
|
||||||
|
tagsToRemove,
|
||||||
|
viewsToAdd,
|
||||||
|
tagsToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tagsToDelete.length; i++) {
|
||||||
|
removeShadowNode(mShadowNodeRegistry.getNode(tagsToDelete[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,22 +485,23 @@ public class UIImplementation {
|
|||||||
public void setChildren(
|
public void setChildren(
|
||||||
int viewTag,
|
int viewTag,
|
||||||
ReadableArray childrenTags) {
|
ReadableArray childrenTags) {
|
||||||
|
synchronized (uiImplementationThreadLock) {
|
||||||
|
ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag);
|
||||||
|
|
||||||
ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag);
|
for (int i = 0; i < childrenTags.size(); i++) {
|
||||||
|
ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(childrenTags.getInt(i));
|
||||||
for (int i = 0; i < childrenTags.size(); i++) {
|
if (cssNodeToAdd == null) {
|
||||||
ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(childrenTags.getInt(i));
|
throw new IllegalViewOperationException("Trying to add unknown view tag: "
|
||||||
if (cssNodeToAdd == null) {
|
+ childrenTags.getInt(i));
|
||||||
throw new IllegalViewOperationException("Trying to add unknown view tag: "
|
}
|
||||||
+ childrenTags.getInt(i));
|
cssNodeToManage.addChildAt(cssNodeToAdd, i);
|
||||||
}
|
}
|
||||||
cssNodeToManage.addChildAt(cssNodeToAdd, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cssNodeToManage.isVirtual() && !cssNodeToManage.isVirtualAnchor()) {
|
if (!cssNodeToManage.isVirtual() && !cssNodeToManage.isVirtualAnchor()) {
|
||||||
mNativeViewHierarchyOptimizer.handleSetChildren(
|
mNativeViewHierarchyOptimizer.handleSetChildren(
|
||||||
cssNodeToManage,
|
cssNodeToManage,
|
||||||
childrenTags);
|
childrenTags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user