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 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 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 65 ReanimatedUIManagerBinding::~ReanimatedUIManagerBinding() {} 66 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 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 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