1*5f154df9Saleqsio #ifdef RCT_NEW_ARCH_ENABLED
2*5f154df9Saleqsio 
3*5f154df9Saleqsio #include "ShadowTreeCloner.h"
4*5f154df9Saleqsio #include "FabricUtils.h"
5*5f154df9Saleqsio 
6*5f154df9Saleqsio namespace reanimated {
7*5f154df9Saleqsio 
ShadowTreeCloner(std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry,std::shared_ptr<UIManager> uiManager,SurfaceId surfaceId)8*5f154df9Saleqsio ShadowTreeCloner::ShadowTreeCloner(
9*5f154df9Saleqsio     std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry,
10*5f154df9Saleqsio     std::shared_ptr<UIManager> uiManager,
11*5f154df9Saleqsio     SurfaceId surfaceId)
12*5f154df9Saleqsio     : newestShadowNodesRegistry_{newestShadowNodesRegistry},
13*5f154df9Saleqsio       propsParserContext_{
14*5f154df9Saleqsio           surfaceId,
15*5f154df9Saleqsio           *getContextContainerFromUIManager(&*uiManager)} {}
16*5f154df9Saleqsio 
~ShadowTreeCloner()17*5f154df9Saleqsio ShadowTreeCloner::~ShadowTreeCloner() {
18*5f154df9Saleqsio #ifdef DEBUG
19*5f154df9Saleqsio   react_native_assert(
20*5f154df9Saleqsio       yogaChildrenUpdates_.empty() &&
21*5f154df9Saleqsio       "Deallocating `ShadowTreeCloner` without calling `updateYogaChildren`.");
22*5f154df9Saleqsio #endif
23*5f154df9Saleqsio }
24*5f154df9Saleqsio 
cloneWithNewProps(const ShadowNode::Shared & oldRootNode,const ShadowNodeFamily & family,RawProps && rawProps)25*5f154df9Saleqsio ShadowNode::Unshared ShadowTreeCloner::cloneWithNewProps(
26*5f154df9Saleqsio     const ShadowNode::Shared &oldRootNode,
27*5f154df9Saleqsio     const ShadowNodeFamily &family,
28*5f154df9Saleqsio     RawProps &&rawProps) {
29*5f154df9Saleqsio   // adapted from ShadowNode::cloneTree
30*5f154df9Saleqsio 
31*5f154df9Saleqsio   auto ancestors = family.getAncestors(*oldRootNode);
32*5f154df9Saleqsio 
33*5f154df9Saleqsio   if (ancestors.empty()) {
34*5f154df9Saleqsio     return ShadowNode::Unshared{nullptr};
35*5f154df9Saleqsio   }
36*5f154df9Saleqsio 
37*5f154df9Saleqsio   auto &parent = ancestors.back();
38*5f154df9Saleqsio   auto &oldShadowNode = parent.first.get().getChildren().at(parent.second);
39*5f154df9Saleqsio 
40*5f154df9Saleqsio   const auto newest = newestShadowNodesRegistry_->get(oldShadowNode->getTag());
41*5f154df9Saleqsio 
42*5f154df9Saleqsio   const auto &source = newest == nullptr ? oldShadowNode : newest;
43*5f154df9Saleqsio 
44*5f154df9Saleqsio   const auto props = source->getComponentDescriptor().cloneProps(
45*5f154df9Saleqsio       propsParserContext_, source->getProps(), rawProps);
46*5f154df9Saleqsio 
47*5f154df9Saleqsio   auto newChildNode = source->clone({/* .props = */ props});
48*5f154df9Saleqsio 
49*5f154df9Saleqsio   for (auto it = ancestors.rbegin(); it != ancestors.rend(); ++it) {
50*5f154df9Saleqsio     auto &parentNode = it->first.get();
51*5f154df9Saleqsio     auto childIndex = it->second;
52*5f154df9Saleqsio 
53*5f154df9Saleqsio     auto children = parentNode.getChildren();
54*5f154df9Saleqsio     const auto &oldChildNode = *children.at(childIndex);
55*5f154df9Saleqsio     react_native_assert(ShadowNode::sameFamily(oldChildNode, *newChildNode));
56*5f154df9Saleqsio 
57*5f154df9Saleqsio     newestShadowNodesRegistry_->set(newChildNode, parentNode.getTag());
58*5f154df9Saleqsio 
59*5f154df9Saleqsio     if (!parentNode.getSealed()) {
60*5f154df9Saleqsio       // Optimization: if a ShadowNode is unsealed, we can directly update its
61*5f154df9Saleqsio       // children instead of cloning the whole path to the root node.
62*5f154df9Saleqsio       auto &parentNodeNonConst = const_cast<ShadowNode &>(parentNode);
63*5f154df9Saleqsio       parentNodeNonConst.replaceChild(oldChildNode, newChildNode, childIndex);
64*5f154df9Saleqsio       yogaChildrenUpdates_.insert(&parentNodeNonConst);
65*5f154df9Saleqsio       return std::const_pointer_cast<ShadowNode>(oldRootNode);
66*5f154df9Saleqsio     }
67*5f154df9Saleqsio 
68*5f154df9Saleqsio     children[childIndex] = newChildNode;
69*5f154df9Saleqsio 
70*5f154df9Saleqsio     newChildNode = parentNode.clone({
71*5f154df9Saleqsio         ShadowNodeFragment::propsPlaceholder(),
72*5f154df9Saleqsio         std::make_shared<ShadowNode::ListOfShared>(children),
73*5f154df9Saleqsio     });
74*5f154df9Saleqsio   }
75*5f154df9Saleqsio 
76*5f154df9Saleqsio   return std::const_pointer_cast<ShadowNode>(newChildNode);
77*5f154df9Saleqsio }
78*5f154df9Saleqsio 
updateYogaChildren()79*5f154df9Saleqsio void ShadowTreeCloner::updateYogaChildren() {
80*5f154df9Saleqsio   // Unfortunately, `replaceChild` does not update Yoga nodes, so we need to
81*5f154df9Saleqsio   // update them manually here.
82*5f154df9Saleqsio   for (ShadowNode *shadowNode : yogaChildrenUpdates_) {
83*5f154df9Saleqsio     static_cast<YogaLayoutableShadowNode *>(shadowNode)->updateYogaChildren();
84*5f154df9Saleqsio   }
85*5f154df9Saleqsio #ifdef DEBUG
86*5f154df9Saleqsio   yogaChildrenUpdates_.clear();
87*5f154df9Saleqsio #endif
88*5f154df9Saleqsio }
89*5f154df9Saleqsio 
90*5f154df9Saleqsio } // namespace reanimated
91*5f154df9Saleqsio 
92*5f154df9Saleqsio #endif // RCT_NEW_ARCH_ENABLED
93