1 #include "NativeReanimatedModule.h" 2 3 #ifdef RCT_NEW_ARCH_ENABLED 4 #include <react/renderer/core/TraitCast.h> 5 #include <react/renderer/uimanager/UIManagerBinding.h> 6 #include <react/renderer/uimanager/primitives.h> 7 #endif 8 9 #include <functional> 10 #include <memory> 11 #include <thread> 12 #include <unordered_map> 13 14 #ifdef RCT_NEW_ARCH_ENABLED 15 #include "FabricUtils.h" 16 #include "NewestShadowNodesRegistry.h" 17 #include "ReanimatedUIManagerBinding.h" 18 #include "ShadowTreeCloner.h" 19 #endif 20 21 #include "EventHandlerRegistry.h" 22 #include "FeaturesConfig.h" 23 #include "ReanimatedHiddenHeaders.h" 24 #include "RuntimeDecorator.h" 25 #include "Shareables.h" 26 #include "WorkletEventHandler.h" 27 28 using namespace facebook; 29 30 namespace reanimated { 31 32 NativeReanimatedModule::NativeReanimatedModule( 33 const std::shared_ptr<CallInvoker> &jsInvoker, 34 const std::shared_ptr<Scheduler> &scheduler, 35 const std::shared_ptr<jsi::Runtime> &rt, 36 const std::shared_ptr<ErrorHandler> &errorHandler, 37 #ifdef RCT_NEW_ARCH_ENABLED 38 // nothing 39 #else 40 std::function<jsi::Value(jsi::Runtime &, const int, const jsi::String &)> 41 propObtainer, 42 #endif 43 PlatformDepMethodsHolder platformDepMethodsHolder) 44 : NativeReanimatedModuleSpec(jsInvoker), 45 RuntimeManager(rt, errorHandler, scheduler, RuntimeType::UI), 46 eventHandlerRegistry(std::make_unique<EventHandlerRegistry>()), 47 requestRender(platformDepMethodsHolder.requestRender), 48 #ifdef RCT_NEW_ARCH_ENABLED 49 // nothing 50 #else 51 propObtainer(propObtainer), 52 #endif 53 animatedSensorModule(platformDepMethodsHolder), 54 #ifdef RCT_NEW_ARCH_ENABLED 55 synchronouslyUpdateUIPropsFunction( 56 platformDepMethodsHolder.synchronouslyUpdateUIPropsFunction) 57 #else 58 configurePropsPlatformFunction( 59 platformDepMethodsHolder.configurePropsFunction) 60 #endif 61 { 62 auto requestAnimationFrame = [=](jsi::Runtime &rt, const jsi::Value &fn) { 63 auto jsFunction = std::make_shared<jsi::Value>(rt, fn); 64 frameCallbacks.push_back([=](double timestamp) { 65 runtimeHelper->runOnUIGuarded(*jsFunction, jsi::Value(timestamp)); 66 }); 67 maybeRequestRender(); 68 }; 69 70 auto scheduleOnJS = [this]( 71 jsi::Runtime &rt, 72 const jsi::Value &remoteFun, 73 const jsi::Value &argsValue) { 74 auto shareableRemoteFun = extractShareableOrThrow<ShareableRemoteFunction>( 75 rt, 76 remoteFun, 77 "Incompatible object passed to scheduleOnJS. It is only allowed to schedule functions defined on the React Native JS runtime this way."); 78 auto shareableArgs = argsValue.isUndefined() 79 ? nullptr 80 : extractShareableOrThrow(rt, argsValue); 81 auto jsRuntime = this->runtimeHelper->rnRuntime(); 82 this->scheduler->scheduleOnJS([=] { 83 jsi::Runtime &rt = *jsRuntime; 84 auto remoteFun = shareableRemoteFun->getJSValue(rt); 85 if (shareableArgs == nullptr) { 86 // fast path for remote function w/o arguments 87 remoteFun.asObject(rt).asFunction(rt).call(rt); 88 } else { 89 auto argsArray = shareableArgs->getJSValue(rt).asObject(rt).asArray(rt); 90 auto argsSize = argsArray.size(rt); 91 // number of arguments is typically relatively small so it is ok to 92 // to use VLAs here, hence disabling the lint rule 93 jsi::Value args[argsSize]; // NOLINT(runtime/arrays) 94 for (size_t i = 0; i < argsSize; i++) { 95 args[i] = argsArray.getValueAtIndex(rt, i); 96 } 97 remoteFun.asObject(rt).asFunction(rt).call(rt, args, argsSize); 98 } 99 }); 100 }; 101 102 auto makeShareableClone = [this](jsi::Runtime &rt, const jsi::Value &value) { 103 return this->makeShareableClone(rt, value, jsi::Value::undefined()); 104 }; 105 106 auto updateDataSynchronously = 107 [this]( 108 jsi::Runtime &rt, 109 const jsi::Value &synchronizedDataHolderRef, 110 const jsi::Value &newData) { 111 return this->updateDataSynchronously( 112 rt, synchronizedDataHolderRef, newData); 113 }; 114 115 #ifdef RCT_NEW_ARCH_ENABLED 116 auto updateProps = [this]( 117 jsi::Runtime &rt, 118 const jsi::Value &shadowNodeValue, 119 const jsi::Value &props) { 120 this->updateProps(rt, shadowNodeValue, props); 121 }; 122 123 auto removeShadowNodeFromRegistry = 124 [this](jsi::Runtime &rt, const jsi::Value &tag) { 125 this->removeShadowNodeFromRegistry(rt, tag); 126 }; 127 128 auto measure = [this](jsi::Runtime &rt, const jsi::Value &shadowNodeValue) { 129 return this->measure(rt, shadowNodeValue); 130 }; 131 132 auto dispatchCommand = [this]( 133 jsi::Runtime &rt, 134 const jsi::Value &shadowNodeValue, 135 const jsi::Value &commandNameValue, 136 const jsi::Value &argsValue) { 137 this->dispatchCommand(rt, shadowNodeValue, commandNameValue, argsValue); 138 }; 139 #endif 140 141 RuntimeDecorator::decorateUIRuntime( 142 *runtime, 143 #ifdef RCT_NEW_ARCH_ENABLED 144 updateProps, 145 measure, 146 removeShadowNodeFromRegistry, 147 dispatchCommand, 148 #else 149 platformDepMethodsHolder.updatePropsFunction, 150 platformDepMethodsHolder.measureFunction, 151 platformDepMethodsHolder.scrollToFunction, 152 #endif 153 requestAnimationFrame, 154 scheduleOnJS, 155 makeShareableClone, 156 updateDataSynchronously, 157 platformDepMethodsHolder.getCurrentTime, 158 platformDepMethodsHolder.setGestureStateFunction, 159 platformDepMethodsHolder.progressLayoutAnimation, 160 platformDepMethodsHolder.endLayoutAnimation, 161 platformDepMethodsHolder.maybeFlushUIUpdatesQueueFunction); 162 onRenderCallback = [this](double timestampMs) { 163 this->renderRequested = false; 164 this->onRender(timestampMs); 165 }; 166 167 #ifdef RCT_NEW_ARCH_ENABLED 168 // nothing 169 #else 170 updatePropsFunction = platformDepMethodsHolder.updatePropsFunction; 171 #endif 172 subscribeForKeyboardEventsFunction = 173 platformDepMethodsHolder.subscribeForKeyboardEvents; 174 unsubscribeFromKeyboardEventsFunction = 175 platformDepMethodsHolder.unsubscribeFromKeyboardEvents; 176 } 177 178 void NativeReanimatedModule::installCoreFunctions( 179 jsi::Runtime &rt, 180 const jsi::Value &callGuard, 181 const jsi::Value &valueUnpacker) { 182 if (!runtimeHelper) { 183 // initialize runtimeHelper here if not already present. We expect only one 184 // instace of the helper to exists. 185 runtimeHelper = 186 std::make_shared<JSRuntimeHelper>(&rt, this->runtime.get(), scheduler); 187 } 188 runtimeHelper->callGuard = 189 std::make_unique<CoreFunction>(runtimeHelper.get(), callGuard); 190 runtimeHelper->valueUnpacker = 191 std::make_unique<CoreFunction>(runtimeHelper.get(), valueUnpacker); 192 } 193 194 NativeReanimatedModule::~NativeReanimatedModule() { 195 if (runtimeHelper) { 196 runtimeHelper->callGuard = nullptr; 197 runtimeHelper->valueUnpacker = nullptr; 198 // event handler registry and frame callbacks store some JSI values from UI 199 // runtime, so they have to go away before we tear down the runtime 200 eventHandlerRegistry.reset(); 201 frameCallbacks.clear(); 202 runtime.reset(); 203 // make sure uiRuntimeDestroyed is set after the runtime is deallocated 204 runtimeHelper->uiRuntimeDestroyed = true; 205 } 206 } 207 208 void NativeReanimatedModule::scheduleOnUI( 209 jsi::Runtime &rt, 210 const jsi::Value &worklet) { 211 auto shareableWorklet = extractShareableOrThrow(rt, worklet); 212 assert( 213 shareableWorklet->valueType() == Shareable::WorkletType && 214 "only worklets can be scheduled to run on UI"); 215 scheduler->scheduleOnUI([=] { 216 jsi::Runtime &rt = *runtimeHelper->uiRuntime(); 217 auto workletValue = shareableWorklet->getJSValue(rt); 218 runtimeHelper->runOnUIGuarded(workletValue); 219 }); 220 } 221 222 jsi::Value NativeReanimatedModule::makeSynchronizedDataHolder( 223 jsi::Runtime &rt, 224 const jsi::Value &initialShareable) { 225 auto dataHolder = std::make_shared<ShareableSynchronizedDataHolder>( 226 runtimeHelper, rt, initialShareable); 227 return dataHolder->getJSValue(rt); 228 } 229 230 void NativeReanimatedModule::updateDataSynchronously( 231 jsi::Runtime &rt, 232 const jsi::Value &synchronizedDataHolderRef, 233 const jsi::Value &newData) { 234 auto dataHolder = extractShareableOrThrow<ShareableSynchronizedDataHolder>( 235 rt, synchronizedDataHolderRef); 236 dataHolder->set(rt, newData); 237 } 238 239 jsi::Value NativeReanimatedModule::getDataSynchronously( 240 jsi::Runtime &rt, 241 const jsi::Value &synchronizedDataHolderRef) { 242 auto dataHolder = extractShareableOrThrow<ShareableSynchronizedDataHolder>( 243 rt, synchronizedDataHolderRef); 244 return dataHolder->get(rt); 245 } 246 247 jsi::Value NativeReanimatedModule::makeShareableClone( 248 jsi::Runtime &rt, 249 const jsi::Value &value, 250 const jsi::Value &shouldRetainRemote) { 251 std::shared_ptr<Shareable> shareable; 252 if (value.isObject()) { 253 auto object = value.asObject(rt); 254 if (!object.getProperty(rt, "__workletHash").isUndefined()) { 255 shareable = std::make_shared<ShareableWorklet>(runtimeHelper, rt, object); 256 } else if (!object.getProperty(rt, "__init").isUndefined()) { 257 shareable = std::make_shared<ShareableHandle>(runtimeHelper, rt, object); 258 } else if (object.isFunction(rt)) { 259 auto function = object.asFunction(rt); 260 if (function.isHostFunction(rt)) { 261 shareable = 262 std::make_shared<ShareableHostFunction>(rt, std::move(function)); 263 } else { 264 shareable = std::make_shared<ShareableRemoteFunction>( 265 runtimeHelper, rt, std::move(function)); 266 } 267 } else if (object.isArray(rt)) { 268 if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) { 269 shareable = std::make_shared<RetainingShareable<ShareableArray>>( 270 runtimeHelper, rt, object.asArray(rt)); 271 } else { 272 shareable = std::make_shared<ShareableArray>(rt, object.asArray(rt)); 273 } 274 } else if (object.isHostObject(rt)) { 275 shareable = std::make_shared<ShareableHostObject>( 276 runtimeHelper, rt, object.getHostObject(rt)); 277 } else { 278 if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) { 279 shareable = std::make_shared<RetainingShareable<ShareableObject>>( 280 runtimeHelper, rt, object); 281 } else { 282 shareable = std::make_shared<ShareableObject>(rt, object); 283 } 284 } 285 } else if (value.isString()) { 286 shareable = std::make_shared<ShareableString>(value.asString(rt).utf8(rt)); 287 } else if (value.isUndefined()) { 288 shareable = std::make_shared<ShareableScalar>(); 289 } else if (value.isNull()) { 290 shareable = std::make_shared<ShareableScalar>(nullptr); 291 } else if (value.isBool()) { 292 shareable = std::make_shared<ShareableScalar>(value.getBool()); 293 } else if (value.isNumber()) { 294 shareable = std::make_shared<ShareableScalar>(value.getNumber()); 295 } else if (value.isSymbol()) { 296 // TODO: this is only a placeholder implementation, here we replace symbols 297 // with strings in order to make certain objects to be captured. There isn't 298 // yet any usecase for using symbols on the UI runtime so it is fine to keep 299 // it like this for now. 300 shareable = 301 std::make_shared<ShareableString>(value.getSymbol(rt).toString(rt)); 302 } else { 303 throw std::runtime_error("attempted to convert an unsupported value type"); 304 } 305 return ShareableJSRef::newHostObject(rt, shareable); 306 } 307 308 jsi::Value NativeReanimatedModule::registerEventHandler( 309 jsi::Runtime &rt, 310 const jsi::Value &eventHash, 311 const jsi::Value &worklet) { 312 static uint64_t EVENT_HANDLER_ID = 1; 313 314 uint64_t newRegistrationId = EVENT_HANDLER_ID++; 315 auto eventName = eventHash.asString(rt).utf8(rt); 316 auto handlerShareable = extractShareableOrThrow(rt, worklet); 317 318 scheduler->scheduleOnUI([=] { 319 jsi::Runtime &rt = *runtimeHelper->uiRuntime(); 320 auto handlerFunction = handlerShareable->getJSValue(rt); 321 auto handler = std::make_shared<WorkletEventHandler>( 322 runtimeHelper, 323 newRegistrationId, 324 eventName, 325 std::move(handlerFunction)); 326 eventHandlerRegistry->registerEventHandler(std::move(handler)); 327 }); 328 329 return jsi::Value(static_cast<double>(newRegistrationId)); 330 } 331 332 void NativeReanimatedModule::unregisterEventHandler( 333 jsi::Runtime &rt, 334 const jsi::Value ®istrationId) { 335 uint64_t id = registrationId.asNumber(); 336 scheduler->scheduleOnUI( 337 [=] { eventHandlerRegistry->unregisterEventHandler(id); }); 338 } 339 340 jsi::Value NativeReanimatedModule::getViewProp( 341 jsi::Runtime &rt, 342 const jsi::Value &viewTag, 343 const jsi::Value &propName, 344 const jsi::Value &callback) { 345 const int viewTagInt = static_cast<int>(viewTag.asNumber()); 346 std::string propNameStr = propName.asString(rt).utf8(rt); 347 jsi::Function fun = callback.getObject(rt).asFunction(rt); 348 std::shared_ptr<jsi::Function> funPtr = 349 std::make_shared<jsi::Function>(std::move(fun)); 350 351 scheduler->scheduleOnUI([&rt, viewTagInt, funPtr, this, propNameStr]() { 352 const jsi::String propNameValue = 353 jsi::String::createFromUtf8(rt, propNameStr); 354 jsi::Value result = propObtainer(rt, viewTagInt, propNameValue); 355 std::string resultStr = result.asString(rt).utf8(rt); 356 357 scheduler->scheduleOnJS([&rt, resultStr, funPtr]() { 358 const jsi::String resultValue = 359 jsi::String::createFromUtf8(rt, resultStr); 360 funPtr->call(rt, resultValue); 361 }); 362 }); 363 364 return jsi::Value::undefined(); 365 } 366 367 jsi::Value NativeReanimatedModule::enableLayoutAnimations( 368 jsi::Runtime &rt, 369 const jsi::Value &config) { 370 FeaturesConfig::setLayoutAnimationEnabled(config.getBool()); 371 return jsi::Value::undefined(); 372 } 373 374 jsi::Value NativeReanimatedModule::configureProps( 375 jsi::Runtime &rt, 376 const jsi::Value &uiProps, 377 const jsi::Value &nativeProps) { 378 #ifdef RCT_NEW_ARCH_ENABLED 379 jsi::Array array = nativeProps.asObject(rt).asArray(rt); 380 for (int i = 0; i < array.size(rt); ++i) { 381 std::string name = array.getValueAtIndex(rt, i).asString(rt).utf8(rt); 382 nativePropNames_.insert(name); 383 } 384 #else 385 configurePropsPlatformFunction(rt, uiProps, nativeProps); 386 #endif // RCT_NEW_ARCH_ENABLED 387 388 return jsi::Value::undefined(); 389 } 390 391 jsi::Value NativeReanimatedModule::configureLayoutAnimation( 392 jsi::Runtime &rt, 393 const jsi::Value &viewTag, 394 const jsi::Value &type, 395 const jsi::Value &sharedTransitionTag, 396 const jsi::Value &config) { 397 layoutAnimationsManager_.configureAnimation( 398 viewTag.asNumber(), 399 static_cast<LayoutAnimationType>(type.asNumber()), 400 sharedTransitionTag.asString(rt).utf8(rt), 401 extractShareableOrThrow(rt, config)); 402 return jsi::Value::undefined(); 403 } 404 405 void NativeReanimatedModule::onEvent( 406 double eventTimestamp, 407 const std::string &eventName, 408 const jsi::Value &payload) { 409 eventHandlerRegistry->processEvent( 410 *runtime, eventTimestamp, eventName, payload); 411 } 412 413 bool NativeReanimatedModule::isAnyHandlerWaitingForEvent( 414 std::string eventName) { 415 return eventHandlerRegistry->isAnyHandlerWaitingForEvent(eventName); 416 } 417 418 void NativeReanimatedModule::maybeRequestRender() { 419 if (!renderRequested) { 420 renderRequested = true; 421 requestRender(onRenderCallback, *this->runtime); 422 } 423 } 424 425 void NativeReanimatedModule::onRender(double timestampMs) { 426 std::vector<FrameCallback> callbacks = frameCallbacks; 427 frameCallbacks.clear(); 428 for (auto &callback : callbacks) { 429 callback(timestampMs); 430 } 431 } 432 433 jsi::Value NativeReanimatedModule::registerSensor( 434 jsi::Runtime &rt, 435 const jsi::Value &sensorType, 436 const jsi::Value &interval, 437 const jsi::Value &iosReferenceFrame, 438 const jsi::Value &sensorDataHandler) { 439 return animatedSensorModule.registerSensor( 440 rt, 441 runtimeHelper, 442 sensorType, 443 interval, 444 iosReferenceFrame, 445 sensorDataHandler); 446 } 447 448 void NativeReanimatedModule::unregisterSensor( 449 jsi::Runtime &rt, 450 const jsi::Value &sensorId) { 451 animatedSensorModule.unregisterSensor(sensorId); 452 } 453 454 void NativeReanimatedModule::cleanupSensors() { 455 animatedSensorModule.unregisterAllSensors(); 456 } 457 458 #ifdef RCT_NEW_ARCH_ENABLED 459 bool NativeReanimatedModule::isThereAnyLayoutProp( 460 jsi::Runtime &rt, 461 const jsi::Value &props) { 462 const jsi::Array propNames = props.asObject(rt).getPropertyNames(rt); 463 for (size_t i = 0; i < propNames.size(rt); ++i) { 464 const std::string propName = 465 propNames.getValueAtIndex(rt, i).asString(rt).utf8(rt); 466 bool isLayoutProp = 467 nativePropNames_.find(propName) != nativePropNames_.end(); 468 if (isLayoutProp) { 469 return true; 470 } 471 } 472 return false; 473 } 474 #endif // RCT_NEW_ARCH_ENABLED 475 476 bool NativeReanimatedModule::handleEvent( 477 const std::string &eventName, 478 const jsi::Value &payload, 479 double currentTime) { 480 onEvent(currentTime, eventName, payload); 481 482 // TODO: return true if Reanimated successfully handled the event 483 // to avoid sending it to JavaScript 484 return false; 485 } 486 487 #ifdef RCT_NEW_ARCH_ENABLED 488 bool NativeReanimatedModule::handleRawEvent( 489 const RawEvent &rawEvent, 490 double currentTime) { 491 const EventTarget *eventTarget = rawEvent.eventTarget.get(); 492 if (eventTarget == nullptr) { 493 // after app reload scrollview is unmounted and its content offset is set to 494 // 0 and view is thrown into recycle pool setting content offset triggers 495 // scroll event eventTarget is null though, because it's unmounting we can 496 // just ignore this event, because it's an event on unmounted component 497 return false; 498 } 499 const std::string &type = rawEvent.type; 500 const ValueFactory &payloadFactory = rawEvent.payloadFactory; 501 502 int tag = eventTarget->getTag(); 503 std::string eventType = type; 504 if (eventType.rfind("top", 0) == 0) { 505 eventType = "on" + eventType.substr(3); 506 } 507 std::string eventName = std::to_string(tag) + eventType; 508 jsi::Runtime &rt = *runtime.get(); 509 jsi::Value payload = payloadFactory(rt); 510 511 auto res = handleEvent(eventName, std::move(payload), currentTime); 512 // TODO: we should call performOperations conditionally if event is handled 513 // (res == true), but for now handleEvent always returns false. Thankfully, 514 // performOperations does not trigger a lot of code if there is nothing to be 515 // done so this is fine for now. 516 performOperations(); 517 return res; 518 } 519 520 void NativeReanimatedModule::updateProps( 521 jsi::Runtime &rt, 522 const jsi::Value &shadowNodeValue, 523 const jsi::Value &props) { 524 ShadowNode::Shared shadowNode = shadowNodeFromValue(rt, shadowNodeValue); 525 526 // TODO: support multiple surfaces 527 surfaceId_ = shadowNode->getSurfaceId(); 528 529 if (isThereAnyLayoutProp(rt, props)) { 530 operationsInBatch_.emplace_back( 531 shadowNode, std::make_unique<jsi::Value>(rt, props)); 532 } else { 533 // TODO: batch with layout props changes? 534 Tag tag = shadowNode->getTag(); 535 synchronouslyUpdateUIPropsFunction(rt, tag, props); 536 } 537 } 538 539 void NativeReanimatedModule::performOperations() { 540 if (operationsInBatch_.empty()) { 541 return; 542 } 543 544 auto copiedOperationsQueue = std::move(operationsInBatch_); 545 operationsInBatch_ = 546 std::vector<std::pair<ShadowNode::Shared, std::unique_ptr<jsi::Value>>>(); 547 548 auto copiedTagsToRemove = std::move(tagsToRemove_); 549 tagsToRemove_ = std::vector<Tag>(); 550 551 react_native_assert(uiManager_ != nullptr); 552 const auto &shadowTreeRegistry = uiManager_->getShadowTreeRegistry(); 553 jsi::Runtime &rt = *runtime.get(); 554 555 shadowTreeRegistry.visit(surfaceId_, [&](ShadowTree const &shadowTree) { 556 auto lock = newestShadowNodesRegistry_->createLock(); 557 558 shadowTree.commit( 559 [&](RootShadowNode const &oldRootShadowNode) { 560 auto rootNode = 561 oldRootShadowNode.ShadowNode::clone(ShadowNodeFragment{}); 562 563 ShadowTreeCloner shadowTreeCloner{ 564 newestShadowNodesRegistry_, uiManager_, surfaceId_}; 565 566 for (const auto &pair : copiedOperationsQueue) { 567 const ShadowNodeFamily &family = pair.first->getFamily(); 568 react_native_assert(family.getSurfaceId() == surfaceId_); 569 570 auto newRootNode = shadowTreeCloner.cloneWithNewProps( 571 rootNode, family, RawProps(rt, *pair.second)); 572 573 if (newRootNode == nullptr) { 574 // this happens when React removed the component but Reanimated 575 // still tries to animate it, let's skip update for this specific 576 // component 577 continue; 578 } 579 rootNode = newRootNode; 580 } 581 582 // remove ShadowNodes and its ancestors from NewestShadowNodesRegistry 583 for (auto tag : copiedTagsToRemove) { 584 newestShadowNodesRegistry_->remove(tag); 585 } 586 587 shadowTreeCloner.updateYogaChildren(); 588 589 return std::static_pointer_cast<RootShadowNode>(rootNode); 590 }, 591 {/* default commit options */}); 592 }); 593 } 594 595 void NativeReanimatedModule::removeShadowNodeFromRegistry( 596 jsi::Runtime &rt, 597 const jsi::Value &tag) { 598 tagsToRemove_.push_back(tag.asNumber()); 599 } 600 601 void NativeReanimatedModule::dispatchCommand( 602 jsi::Runtime &rt, 603 const jsi::Value &shadowNodeValue, 604 const jsi::Value &commandNameValue, 605 const jsi::Value &argsValue) { 606 ShadowNode::Shared shadowNode = shadowNodeFromValue(rt, shadowNodeValue); 607 std::string commandName = stringFromValue(rt, commandNameValue); 608 folly::dynamic args = commandArgsFromValue(rt, argsValue); 609 uiManager_->dispatchCommand(shadowNode, commandName, args); 610 } 611 612 jsi::Value NativeReanimatedModule::measure( 613 jsi::Runtime &rt, 614 const jsi::Value &shadowNodeValue) { 615 // based on implementation from UIManagerBinding.cpp 616 617 auto shadowNode = shadowNodeFromValue(rt, shadowNodeValue); 618 auto layoutMetrics = uiManager_->getRelativeLayoutMetrics( 619 *shadowNode, nullptr, {/* .includeTransform = */ true}); 620 621 if (layoutMetrics == EmptyLayoutMetrics) { 622 // Originally, in this case React Native returns `{0, 0, 0, 0, 0, 0}`, most 623 // likely due to the type of measure callback function which accepts just an 624 // array of numbers (not null). In Reanimated, `measure` returns 625 // `MeasuredDimensions | null`. 626 return jsi::Value::null(); 627 } 628 auto newestCloneOfShadowNode = 629 uiManager_->getNewestCloneOfShadowNode(*shadowNode); 630 631 auto layoutableShadowNode = 632 traitCast<LayoutableShadowNode const *>(newestCloneOfShadowNode.get()); 633 facebook::react::Point originRelativeToParent = 634 layoutableShadowNode != nullptr 635 ? layoutableShadowNode->getLayoutMetrics().frame.origin 636 : facebook::react::Point(); 637 638 auto frame = layoutMetrics.frame; 639 640 jsi::Object result(rt); 641 result.setProperty( 642 rt, "x", jsi::Value(static_cast<double>(originRelativeToParent.x))); 643 result.setProperty( 644 rt, "y", jsi::Value(static_cast<double>(originRelativeToParent.y))); 645 result.setProperty( 646 rt, "width", jsi::Value(static_cast<double>(frame.size.width))); 647 result.setProperty( 648 rt, "height", jsi::Value(static_cast<double>(frame.size.height))); 649 result.setProperty( 650 rt, "pageX", jsi::Value(static_cast<double>(frame.origin.x))); 651 result.setProperty( 652 rt, "pageY", jsi::Value(static_cast<double>(frame.origin.y))); 653 return result; 654 } 655 656 void NativeReanimatedModule::setUIManager( 657 std::shared_ptr<UIManager> uiManager) { 658 uiManager_ = uiManager; 659 } 660 661 void NativeReanimatedModule::setNewestShadowNodesRegistry( 662 std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry) { 663 newestShadowNodesRegistry_ = newestShadowNodesRegistry; 664 } 665 #endif // RCT_NEW_ARCH_ENABLED 666 667 jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents( 668 jsi::Runtime &rt, 669 const jsi::Value &handlerWorklet, 670 const jsi::Value &isStatusBarTranslucent) { 671 auto shareableHandler = extractShareableOrThrow(rt, handlerWorklet); 672 return subscribeForKeyboardEventsFunction( 673 [=](int keyboardState, int height) { 674 jsi::Runtime &rt = *runtimeHelper->uiRuntime(); 675 auto handler = shareableHandler->getJSValue(rt); 676 runtimeHelper->runOnUIGuarded( 677 handler, jsi::Value(keyboardState), jsi::Value(height)); 678 }, 679 isStatusBarTranslucent.getBool()); 680 } 681 682 void NativeReanimatedModule::unsubscribeFromKeyboardEvents( 683 jsi::Runtime &rt, 684 const jsi::Value &listenerId) { 685 unsubscribeFromKeyboardEventsFunction(listenerId.asNumber()); 686 } 687 688 } // namespace reanimated 689