1 #ifdef RCT_NEW_ARCH_ENABLED
2 
3 #include "ReanimatedUIManagerBinding.h"
4 #include "FabricUtils.h"
5 #include "NewestShadowNodesRegistry.h"
6 
7 #include <react/renderer/debug/SystraceSection.h>
8 
9 #include <utility>
10 
11 using namespace facebook;
12 using namespace react;
13 
14 namespace reanimated {
15 
createAndInstallIfNeeded(jsi::Runtime & runtime,RuntimeExecutor const & runtimeExecutor,std::shared_ptr<UIManager> const & uiManager,std::shared_ptr<NewestShadowNodesRegistry> const & newestShadowNodesRegistry)16 void ReanimatedUIManagerBinding::createAndInstallIfNeeded(
17     jsi::Runtime &runtime,
18     RuntimeExecutor const &runtimeExecutor,
19     std::shared_ptr<UIManager> const &uiManager,
20     std::shared_ptr<NewestShadowNodesRegistry> const
21         &newestShadowNodesRegistry) {
22   // adapted from UIManagerBinding.cpp
23   auto uiManagerModuleName = "nativeFabricUIManager";
24 
25   auto eventHandler = [&]() -> std::unique_ptr<EventHandler const> {
26     auto uiManagerValue =
27         runtime.global().getProperty(runtime, uiManagerModuleName);
28     if (uiManagerValue.isUndefined()) {
29       return nullptr;
30     }
31 
32     auto uiManagerBinding =
33         uiManagerValue.asObject(runtime).asHostObject<UIManagerBinding>(
34             runtime);
35     auto uiManagerBindingPublic =
36         reinterpret_cast<UIManagerBindingPublic *>(&*uiManagerBinding);
37     return std::move(uiManagerBindingPublic->eventHandler_);
38   }();
39 
40   auto reanimatedUiManagerBinding =
41       std::make_shared<ReanimatedUIManagerBinding>(
42           uiManager,
43           runtimeExecutor,
44           std::move(eventHandler),
45           newestShadowNodesRegistry);
46   auto object =
47       jsi::Object::createFromHostObject(runtime, reanimatedUiManagerBinding);
48   runtime.global().setProperty(runtime, uiManagerModuleName, std::move(object));
49 }
50 
ReanimatedUIManagerBinding(std::shared_ptr<UIManager> uiManager,RuntimeExecutor runtimeExecutor,std::unique_ptr<EventHandler const> eventHandler,std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry)51 ReanimatedUIManagerBinding::ReanimatedUIManagerBinding(
52     std::shared_ptr<UIManager> uiManager,
53     RuntimeExecutor runtimeExecutor,
54     std::unique_ptr<EventHandler const> eventHandler,
55     std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry)
56     : UIManagerBinding(uiManager),
57       uiManager_(std::move(uiManager)),
58       newestShadowNodesRegistry_(newestShadowNodesRegistry) {
59   if (eventHandler != nullptr) {
60     reinterpret_cast<UIManagerBindingPublic *>(this)->eventHandler_ =
61         std::move(eventHandler);
62   }
63 }
64 
~ReanimatedUIManagerBinding()65 ReanimatedUIManagerBinding::~ReanimatedUIManagerBinding() {}
66 
cloneNodeUsingNewest(UIManager * uiManager,NewestShadowNodesRegistry * newestShadowNodesRegistry,ShadowNode const & shadowNode,ShadowNode::SharedListOfShared const & children=nullptr,RawProps const * rawProps=nullptr)67 static inline ShadowNode::Shared cloneNodeUsingNewest(
68     UIManager *uiManager,
69     NewestShadowNodesRegistry *newestShadowNodesRegistry,
70     ShadowNode const &shadowNode,
71     ShadowNode::SharedListOfShared const &children = nullptr,
72     RawProps const *rawProps = nullptr) {
73   {
74     auto lock = newestShadowNodesRegistry->createLock();
75     auto newest = newestShadowNodesRegistry->get(shadowNode.getTag());
76     if (newest != nullptr) {
77       // ShadowNode managed by Reanimated, use newest ShadowNode from registry
78       auto clone = uiManager->cloneNode(*newest, children, rawProps);
79       newestShadowNodesRegistry->update(clone);
80       return clone;
81     }
82   } // release lock since we don't need registry anymore
83 
84   // ShadowNode not managed by Reanimated (yet?)
85   return uiManager->cloneNode(shadowNode, children, rawProps);
86 }
87 
appendChildUsingNewest(UIManager * uiManager,NewestShadowNodesRegistry * newestShadowNodesRegistry,const ShadowNode::Shared & parentShadowNode,const ShadowNode::Shared & childShadowNode)88 static inline void appendChildUsingNewest(
89     UIManager *uiManager,
90     NewestShadowNodesRegistry *newestShadowNodesRegistry,
91     const ShadowNode::Shared &parentShadowNode,
92     const ShadowNode::Shared &childShadowNode) {
93   {
94     auto lock = newestShadowNodesRegistry->createLock();
95     auto newestChildShadowNode =
96         newestShadowNodesRegistry->get(childShadowNode->getTag());
97     if (newestChildShadowNode != nullptr) {
98       uiManager->appendChild(parentShadowNode, newestChildShadowNode);
99       return;
100     }
101   } // release lock since we don't need registry anymore
102 
103   // child ShadowNode not managed by Reanimated (yet?)
104   uiManager->appendChild(parentShadowNode, childShadowNode);
105 }
106 
get(jsi::Runtime & runtime,jsi::PropNameID const & name)107 jsi::Value ReanimatedUIManagerBinding::get(
108     jsi::Runtime &runtime,
109     jsi::PropNameID const &name) {
110   // Currently, we need to overwrite all variants of `cloneNode` as well as
111   // `appendChild` to prevent React from overwriting layout props animated using
112   // Reanimated. However, this may degrade performance due to using locks.
113   // We already have an idea how this can be done better without locks
114   // (i.e. by overwriting `completeRoot` and using UIManagerCommitHooks).
115 
116   // based on implementation from UIManagerBinding.cpp
117   auto methodName = name.utf8(runtime);
118   SystraceSection s("ReanimatedUIManagerBinding::get", "name", methodName);
119   UIManager *uiManager = uiManager_.get();
120   NewestShadowNodesRegistry *newestShadowNodesRegistry =
121       newestShadowNodesRegistry_.get();
122 
123   // Semantic: Clones the node with *same* props and *same* children.
124   if (methodName == "cloneNode") {
125     return jsi::Function::createFromHostFunction(
126         runtime,
127         name,
128         1,
129         [uiManager, newestShadowNodesRegistry](
130             jsi::Runtime &runtime,
131             jsi::Value const & /*thisValue*/,
132             jsi::Value const *arguments,
133             size_t /*count*/) noexcept -> jsi::Value {
134           return valueFromShadowNode(
135               runtime,
136               cloneNodeUsingNewest(
137                   uiManager,
138                   newestShadowNodesRegistry,
139                   *shadowNodeFromValue(runtime, arguments[0])));
140         });
141   }
142 
143   // Semantic: Clones the node with *same* props and *empty* children.
144   if (methodName == "cloneNodeWithNewChildren") {
145     return jsi::Function::createFromHostFunction(
146         runtime,
147         name,
148         1,
149         [uiManager, newestShadowNodesRegistry](
150             jsi::Runtime &runtime,
151             jsi::Value const & /*thisValue*/,
152             jsi::Value const *arguments,
153             size_t /*count*/) noexcept -> jsi::Value {
154           return valueFromShadowNode(
155               runtime,
156               cloneNodeUsingNewest(
157                   uiManager,
158                   newestShadowNodesRegistry,
159                   *shadowNodeFromValue(runtime, arguments[0]),
160                   ShadowNode::emptySharedShadowNodeSharedList()));
161         });
162   }
163 
164   // Semantic: Clones the node with *given* props and *same* children.
165   if (methodName == "cloneNodeWithNewProps") {
166     return jsi::Function::createFromHostFunction(
167         runtime,
168         name,
169         2,
170         [uiManager, newestShadowNodesRegistry](
171             jsi::Runtime &runtime,
172             jsi::Value const & /*thisValue*/,
173             jsi::Value const *arguments,
174             size_t /*count*/) noexcept -> jsi::Value {
175           auto const &rawProps = RawProps(runtime, arguments[1]);
176           return valueFromShadowNode(
177               runtime,
178               cloneNodeUsingNewest(
179                   uiManager,
180                   newestShadowNodesRegistry,
181                   *shadowNodeFromValue(runtime, arguments[0]),
182                   nullptr,
183                   &rawProps));
184         });
185   }
186 
187   // Semantic: Clones the node with *given* props and *empty* children.
188   if (methodName == "cloneNodeWithNewChildrenAndProps") {
189     return jsi::Function::createFromHostFunction(
190         runtime,
191         name,
192         2,
193         [uiManager, newestShadowNodesRegistry](
194             jsi::Runtime &runtime,
195             jsi::Value const & /*thisValue*/,
196             jsi::Value const *arguments,
197             size_t /*count*/) noexcept -> jsi::Value {
198           auto const &rawProps = RawProps(runtime, arguments[1]);
199           return valueFromShadowNode(
200               runtime,
201               cloneNodeUsingNewest(
202                   uiManager,
203                   newestShadowNodesRegistry,
204                   *shadowNodeFromValue(runtime, arguments[0]),
205                   ShadowNode::emptySharedShadowNodeSharedList(),
206                   &rawProps));
207         });
208   }
209 
210   if (methodName == "appendChild") {
211     return jsi::Function::createFromHostFunction(
212         runtime,
213         name,
214         2,
215         [uiManager, newestShadowNodesRegistry](
216             jsi::Runtime &runtime,
217             jsi::Value const & /*thisValue*/,
218             jsi::Value const *arguments,
219             size_t /*count*/) noexcept -> jsi::Value {
220           appendChildUsingNewest(
221               uiManager,
222               newestShadowNodesRegistry,
223               shadowNodeFromValue(runtime, arguments[0]),
224               shadowNodeFromValue(runtime, arguments[1]));
225           return jsi::Value::undefined();
226         });
227   }
228 
229   // Methods like "findNodeAtPoint", "getRelativeLayoutMetrics", "measure" etc.
230   // use `UIManager::getNewestCloneOfShadowNode` or
231   // `ShadowTree::getCurrentRevision` under the hood,
232   // so there's no need to overwrite them.
233 
234   return UIManagerBinding::get(runtime, name);
235 }
236 
237 } // namespace reanimated
238 
239 #endif // RCT_NEW_ARCH_ENABLED
240