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