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