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
NativeReanimatedModule(const std::shared_ptr<CallInvoker> & jsInvoker,const std::shared_ptr<Scheduler> & scheduler,const std::shared_ptr<jsi::Runtime> & rt,const std::shared_ptr<ErrorHandler> & errorHandler,PlatformDepMethodsHolder platformDepMethodsHolder)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
installCoreFunctions(jsi::Runtime & rt,const jsi::Value & callGuard,const jsi::Value & valueUnpacker)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
~NativeReanimatedModule()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
scheduleOnUI(jsi::Runtime & rt,const jsi::Value & worklet)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
makeSynchronizedDataHolder(jsi::Runtime & rt,const jsi::Value & initialShareable)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
updateDataSynchronously(jsi::Runtime & rt,const jsi::Value & synchronizedDataHolderRef,const jsi::Value & newData)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
getDataSynchronously(jsi::Runtime & rt,const jsi::Value & synchronizedDataHolderRef)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
makeShareableClone(jsi::Runtime & rt,const jsi::Value & value,const jsi::Value & shouldRetainRemote)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
registerEventHandler(jsi::Runtime & rt,const jsi::Value & eventHash,const jsi::Value & worklet)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
unregisterEventHandler(jsi::Runtime & rt,const jsi::Value & registrationId)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
getViewProp(jsi::Runtime & rt,const jsi::Value & viewTag,const jsi::Value & propName,const jsi::Value & callback)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
enableLayoutAnimations(jsi::Runtime & rt,const jsi::Value & config)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
configureProps(jsi::Runtime & rt,const jsi::Value & uiProps,const jsi::Value & nativeProps)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
configureLayoutAnimation(jsi::Runtime & rt,const jsi::Value & viewTag,const jsi::Value & type,const jsi::Value & sharedTransitionTag,const jsi::Value & config)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
onEvent(double eventTimestamp,const std::string & eventName,const jsi::Value & payload)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
isAnyHandlerWaitingForEvent(std::string eventName)413 bool NativeReanimatedModule::isAnyHandlerWaitingForEvent(
414 std::string eventName) {
415 return eventHandlerRegistry->isAnyHandlerWaitingForEvent(eventName);
416 }
417
maybeRequestRender()418 void NativeReanimatedModule::maybeRequestRender() {
419 if (!renderRequested) {
420 renderRequested = true;
421 requestRender(onRenderCallback, *this->runtime);
422 }
423 }
424
onRender(double timestampMs)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
registerSensor(jsi::Runtime & rt,const jsi::Value & sensorType,const jsi::Value & interval,const jsi::Value & iosReferenceFrame,const jsi::Value & sensorDataHandler)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
unregisterSensor(jsi::Runtime & rt,const jsi::Value & sensorId)448 void NativeReanimatedModule::unregisterSensor(
449 jsi::Runtime &rt,
450 const jsi::Value &sensorId) {
451 animatedSensorModule.unregisterSensor(sensorId);
452 }
453
cleanupSensors()454 void NativeReanimatedModule::cleanupSensors() {
455 animatedSensorModule.unregisterAllSensors();
456 }
457
458 #ifdef RCT_NEW_ARCH_ENABLED
isThereAnyLayoutProp(jsi::Runtime & rt,const jsi::Value & props)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
handleEvent(const std::string & eventName,const jsi::Value & payload,double currentTime)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
handleRawEvent(const RawEvent & rawEvent,double currentTime)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
updateProps(jsi::Runtime & rt,const jsi::Value & shadowNodeValue,const jsi::Value & props)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
performOperations()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
removeShadowNodeFromRegistry(jsi::Runtime & rt,const jsi::Value & tag)595 void NativeReanimatedModule::removeShadowNodeFromRegistry(
596 jsi::Runtime &rt,
597 const jsi::Value &tag) {
598 tagsToRemove_.push_back(tag.asNumber());
599 }
600
dispatchCommand(jsi::Runtime & rt,const jsi::Value & shadowNodeValue,const jsi::Value & commandNameValue,const jsi::Value & argsValue)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
measure(jsi::Runtime & rt,const jsi::Value & shadowNodeValue)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
setUIManager(std::shared_ptr<UIManager> uiManager)656 void NativeReanimatedModule::setUIManager(
657 std::shared_ptr<UIManager> uiManager) {
658 uiManager_ = uiManager;
659 }
660
setNewestShadowNodesRegistry(std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry)661 void NativeReanimatedModule::setNewestShadowNodesRegistry(
662 std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry) {
663 newestShadowNodesRegistry_ = newestShadowNodesRegistry;
664 }
665 #endif // RCT_NEW_ARCH_ENABLED
666
subscribeForKeyboardEvents(jsi::Runtime & rt,const jsi::Value & handlerWorklet,const jsi::Value & isStatusBarTranslucent)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
unsubscribeFromKeyboardEvents(jsi::Runtime & rt,const jsi::Value & listenerId)682 void NativeReanimatedModule::unsubscribeFromKeyboardEvents(
683 jsi::Runtime &rt,
684 const jsi::Value &listenerId) {
685 unsubscribeFromKeyboardEventsFunction(listenerId.asNumber());
686 }
687
688 } // namespace reanimated
689