1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 #include "ABI49_0_0UIManager.h"
9
10 #include <ABI49_0_0React/debug/ABI49_0_0React_native_assert.h>
11 #include <ABI49_0_0React/renderer/core/ABI49_0_0DynamicPropsUtilities.h>
12 #include <ABI49_0_0React/renderer/core/ABI49_0_0PropsParserContext.h>
13 #include <ABI49_0_0React/renderer/core/ABI49_0_0ShadowNodeFragment.h>
14 #include <ABI49_0_0React/renderer/core/ABI49_0_0TraitCast.h>
15 #include <ABI49_0_0React/renderer/debug/ABI49_0_0SystraceSection.h>
16 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0SurfaceRegistryBinding.h>
17 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0UIManagerBinding.h>
18 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0UIManagerCommitHook.h>
19
20 #include <glog/logging.h>
21
22 #include <utility>
23
24 namespace ABI49_0_0facebook::ABI49_0_0React {
25
26 // Explicitly define destructors here, as they to exist in order to act as a
27 // "key function" for the ShadowNodeWrapper class -- this allow for RTTI to work
28 // properly across dynamic library boundaries (i.e. dynamic_cast that is used by
29 // isHostObject method)
30 ShadowNodeWrapper::~ShadowNodeWrapper() = default;
31 ShadowNodeListWrapper::~ShadowNodeListWrapper() = default;
32
constructLeakCheckerIfNeeded(RuntimeExecutor const & runtimeExecutor)33 static std::unique_ptr<LeakChecker> constructLeakCheckerIfNeeded(
34 RuntimeExecutor const &runtimeExecutor) {
35 #ifdef ABI49_0_0REACT_NATIVE_DEBUG
36 return std::make_unique<LeakChecker>(runtimeExecutor);
37 #else
38 return {};
39 #endif
40 }
41
UIManager(RuntimeExecutor const & runtimeExecutor,BackgroundExecutor backgroundExecutor,ContextContainer::Shared contextContainer)42 UIManager::UIManager(
43 RuntimeExecutor const &runtimeExecutor,
44 BackgroundExecutor backgroundExecutor,
45 ContextContainer::Shared contextContainer)
46 : runtimeExecutor_(runtimeExecutor),
47 backgroundExecutor_(std::move(backgroundExecutor)),
48 contextContainer_(std::move(contextContainer)),
49 leakChecker_(constructLeakCheckerIfNeeded(runtimeExecutor)) {}
50
~UIManager()51 UIManager::~UIManager() {
52 LOG(WARNING) << "UIManager::~UIManager() was called (address: " << this
53 << ").";
54 }
55
createNode(Tag tag,std::string const & name,SurfaceId surfaceId,const RawProps & rawProps,SharedEventTarget eventTarget) const56 ShadowNode::Shared UIManager::createNode(
57 Tag tag,
58 std::string const &name,
59 SurfaceId surfaceId,
60 const RawProps &rawProps,
61 SharedEventTarget eventTarget) const {
62 SystraceSection s("UIManager::createNode");
63
64 auto &componentDescriptor = componentDescriptorRegistry_->at(name);
65 auto fallbackDescriptor =
66 componentDescriptorRegistry_->getFallbackComponentDescriptor();
67
68 PropsParserContext propsParserContext{surfaceId, *contextContainer_.get()};
69
70 auto const fragment = ShadowNodeFamilyFragment{tag, surfaceId, nullptr};
71 auto family =
72 componentDescriptor.createFamily(fragment, std::move(eventTarget));
73 auto const props =
74 componentDescriptor.cloneProps(propsParserContext, nullptr, rawProps);
75 auto const state =
76 componentDescriptor.createInitialState(ShadowNodeFragment{props}, family);
77
78 auto shadowNode = componentDescriptor.createShadowNode(
79 ShadowNodeFragment{
80 /* .props = */
81 fallbackDescriptor != nullptr &&
82 fallbackDescriptor->getComponentHandle() ==
83 componentDescriptor.getComponentHandle()
84 ? componentDescriptor.cloneProps(
85 propsParserContext,
86 props,
87 RawProps(folly::dynamic::object("name", name)))
88 : props,
89 /* .children = */ ShadowNodeFragment::childrenPlaceholder(),
90 /* .state = */ state,
91 },
92 family);
93
94 if (delegate_ != nullptr) {
95 delegate_->uiManagerDidCreateShadowNode(*shadowNode);
96 }
97 if (leakChecker_) {
98 leakChecker_->uiManagerDidCreateShadowNodeFamily(family);
99 }
100
101 return shadowNode;
102 }
103
cloneNode(ShadowNode const & shadowNode,ShadowNode::SharedListOfShared const & children,RawProps const * rawProps) const104 ShadowNode::Shared UIManager::cloneNode(
105 ShadowNode const &shadowNode,
106 ShadowNode::SharedListOfShared const &children,
107 RawProps const *rawProps) const {
108 SystraceSection s("UIManager::cloneNode");
109
110 PropsParserContext propsParserContext{
111 shadowNode.getFamily().getSurfaceId(), *contextContainer_.get()};
112
113 auto &componentDescriptor = shadowNode.getComponentDescriptor();
114 auto &family = shadowNode.getFamily();
115 auto props = ShadowNodeFragment::propsPlaceholder();
116
117 if (rawProps != nullptr) {
118 if (family.nativeProps_DEPRECATED != nullptr) {
119 // Values in `rawProps` patch (take precedence over)
120 // `nativeProps_DEPRECATED`. For example, if both `nativeProps_DEPRECATED`
121 // and `rawProps` contain key 'A'. Value from `rawProps` overrides what
122 // was previously in `nativeProps_DEPRECATED`.
123 family.nativeProps_DEPRECATED =
124 std::make_unique<folly::dynamic>(mergeDynamicProps(
125 *family.nativeProps_DEPRECATED, (folly::dynamic)*rawProps));
126
127 props = componentDescriptor.cloneProps(
128 propsParserContext,
129 shadowNode.getProps(),
130 RawProps(*family.nativeProps_DEPRECATED));
131 } else {
132 props = componentDescriptor.cloneProps(
133 propsParserContext, shadowNode.getProps(), *rawProps);
134 }
135 }
136
137 auto clonedShadowNode = componentDescriptor.cloneShadowNode(
138 shadowNode,
139 {
140 /* .props = */ props,
141 /* .children = */ children,
142 });
143
144 return clonedShadowNode;
145 }
146
appendChild(const ShadowNode::Shared & parentShadowNode,const ShadowNode::Shared & childShadowNode) const147 void UIManager::appendChild(
148 const ShadowNode::Shared &parentShadowNode,
149 const ShadowNode::Shared &childShadowNode) const {
150 SystraceSection s("UIManager::appendChild");
151
152 auto &componentDescriptor = parentShadowNode->getComponentDescriptor();
153 componentDescriptor.appendChild(parentShadowNode, childShadowNode);
154 }
155
completeSurface(SurfaceId surfaceId,ShadowNode::UnsharedListOfShared const & rootChildren,ShadowTree::CommitOptions commitOptions) const156 void UIManager::completeSurface(
157 SurfaceId surfaceId,
158 ShadowNode::UnsharedListOfShared const &rootChildren,
159 ShadowTree::CommitOptions commitOptions) const {
160 SystraceSection s("UIManager::completeSurface");
161
162 shadowTreeRegistry_.visit(surfaceId, [&](ShadowTree const &shadowTree) {
163 shadowTree.commit(
164 [&](RootShadowNode const &oldRootShadowNode) {
165 return std::make_shared<RootShadowNode>(
166 oldRootShadowNode,
167 ShadowNodeFragment{
168 /* .props = */ ShadowNodeFragment::propsPlaceholder(),
169 /* .children = */ rootChildren,
170 });
171 },
172 commitOptions);
173 });
174 }
175
setIsJSResponder(ShadowNode::Shared const & shadowNode,bool isJSResponder,bool blockNativeResponder) const176 void UIManager::setIsJSResponder(
177 ShadowNode::Shared const &shadowNode,
178 bool isJSResponder,
179 bool blockNativeResponder) const {
180 if (delegate_ != nullptr) {
181 delegate_->uiManagerDidSetIsJSResponder(
182 shadowNode, isJSResponder, blockNativeResponder);
183 }
184 }
185
startSurface(ShadowTree::Unique && shadowTree,std::string const & moduleName,folly::dynamic const & props,DisplayMode displayMode) const186 void UIManager::startSurface(
187 ShadowTree::Unique &&shadowTree,
188 std::string const &moduleName,
189 folly::dynamic const &props,
190 DisplayMode displayMode) const {
191 SystraceSection s("UIManager::startSurface");
192
193 auto surfaceId = shadowTree->getSurfaceId();
194 shadowTreeRegistry_.add(std::move(shadowTree));
195
196 runtimeExecutor_([=](jsi::Runtime &runtime) {
197 SystraceSection s("UIManager::startSurface::onRuntime");
198 SurfaceRegistryBinding::startSurface(
199 runtime, surfaceId, moduleName, props, displayMode);
200 });
201 }
202
setSurfaceProps(SurfaceId surfaceId,std::string const & moduleName,folly::dynamic const & props,DisplayMode displayMode) const203 void UIManager::setSurfaceProps(
204 SurfaceId surfaceId,
205 std::string const &moduleName,
206 folly::dynamic const &props,
207 DisplayMode displayMode) const {
208 SystraceSection s("UIManager::setSurfaceProps");
209
210 runtimeExecutor_([=](jsi::Runtime &runtime) {
211 SurfaceRegistryBinding::setSurfaceProps(
212 runtime, surfaceId, moduleName, props, displayMode);
213 });
214 }
215
stopSurface(SurfaceId surfaceId) const216 ShadowTree::Unique UIManager::stopSurface(SurfaceId surfaceId) const {
217 SystraceSection s("UIManager::stopSurface");
218
219 // Stop any ongoing animations.
220 stopSurfaceForAnimationDelegate(surfaceId);
221
222 // Waiting for all concurrent commits to be finished and unregistering the
223 // `ShadowTree`.
224 auto shadowTree = getShadowTreeRegistry().remove(surfaceId);
225
226 // We execute JavaScript/ABI49_0_0React part of the process at the very end to minimize
227 // any visible side-effects of stopping the Surface. Any possible commits from
228 // the JavaScript side will not be able to reference a `ShadowTree` and will
229 // fail silently.
230 runtimeExecutor_([=](jsi::Runtime &runtime) {
231 SurfaceRegistryBinding::stopSurface(runtime, surfaceId);
232 });
233
234 if (leakChecker_) {
235 leakChecker_->stopSurface(surfaceId);
236 }
237
238 return shadowTree;
239 }
240
getNewestCloneOfShadowNode(ShadowNode const & shadowNode) const241 ShadowNode::Shared UIManager::getNewestCloneOfShadowNode(
242 ShadowNode const &shadowNode) const {
243 auto ancestorShadowNode = ShadowNode::Shared{};
244 shadowTreeRegistry_.visit(
245 shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
246 ancestorShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
247 });
248
249 if (!ancestorShadowNode) {
250 return nullptr;
251 }
252
253 auto ancestors = shadowNode.getFamily().getAncestors(*ancestorShadowNode);
254
255 if (ancestors.empty()) {
256 return nullptr;
257 }
258
259 auto pair = ancestors.rbegin();
260 return pair->first.get().getChildren().at(pair->second);
261 }
262
findNodeAtPoint(ShadowNode::Shared const & node,Point point) const263 ShadowNode::Shared UIManager::findNodeAtPoint(
264 ShadowNode::Shared const &node,
265 Point point) const {
266 return LayoutableShadowNode::findNodeAtPoint(
267 getNewestCloneOfShadowNode(*node), point);
268 }
269
getRelativeLayoutMetrics(ShadowNode const & shadowNode,ShadowNode const * ancestorShadowNode,LayoutableShadowNode::LayoutInspectingPolicy policy) const270 LayoutMetrics UIManager::getRelativeLayoutMetrics(
271 ShadowNode const &shadowNode,
272 ShadowNode const *ancestorShadowNode,
273 LayoutableShadowNode::LayoutInspectingPolicy policy) const {
274 SystraceSection s("UIManager::getRelativeLayoutMetrics");
275
276 // We might store here an owning pointer to `ancestorShadowNode` to ensure
277 // that the node is not deallocated during method execution lifetime.
278 auto owningAncestorShadowNode = ShadowNode::Shared{};
279
280 if (ancestorShadowNode == nullptr) {
281 shadowTreeRegistry_.visit(
282 shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
283 owningAncestorShadowNode =
284 shadowTree.getCurrentRevision().rootShadowNode;
285 ancestorShadowNode = owningAncestorShadowNode.get();
286 });
287 } else {
288 // It is possible for JavaScript (or other callers) to have a reference
289 // to a previous version of ShadowNodes, but we enforce that
290 // metrics are only calculated on most recently committed versions.
291 owningAncestorShadowNode = getNewestCloneOfShadowNode(*ancestorShadowNode);
292 ancestorShadowNode = owningAncestorShadowNode.get();
293 }
294
295 auto layoutableAncestorShadowNode =
296 traitCast<LayoutableShadowNode const *>(ancestorShadowNode);
297
298 if (layoutableAncestorShadowNode == nullptr) {
299 return EmptyLayoutMetrics;
300 }
301
302 return LayoutableShadowNode::computeRelativeLayoutMetrics(
303 shadowNode.getFamily(), *layoutableAncestorShadowNode, policy);
304 }
305
updateState(StateUpdate const & stateUpdate) const306 void UIManager::updateState(StateUpdate const &stateUpdate) const {
307 auto &callback = stateUpdate.callback;
308 auto &family = stateUpdate.family;
309 auto &componentDescriptor = family->getComponentDescriptor();
310
311 shadowTreeRegistry_.visit(
312 family->getSurfaceId(), [&](ShadowTree const &shadowTree) {
313 shadowTree.commit(
314 [&](RootShadowNode const &oldRootShadowNode) {
315 auto isValid = true;
316
317 auto rootNode = oldRootShadowNode.cloneTree(
318 *family, [&](ShadowNode const &oldShadowNode) {
319 auto newData =
320 callback(oldShadowNode.getState()->getDataPointer());
321
322 if (!newData) {
323 isValid = false;
324 // Just return something, we will discard it anyway.
325 return oldShadowNode.clone({});
326 }
327
328 auto newState =
329 componentDescriptor.createState(*family, newData);
330
331 return oldShadowNode.clone({
332 /* .props = */ ShadowNodeFragment::propsPlaceholder(),
333 /* .children = */
334 ShadowNodeFragment::childrenPlaceholder(),
335 /* .state = */ newState,
336 });
337 });
338
339 return isValid
340 ? std::static_pointer_cast<RootShadowNode>(rootNode)
341 : nullptr;
342 },
343 {/* default commit options */});
344 });
345 }
346
dispatchCommand(const ShadowNode::Shared & shadowNode,std::string const & commandName,folly::dynamic const & args) const347 void UIManager::dispatchCommand(
348 const ShadowNode::Shared &shadowNode,
349 std::string const &commandName,
350 folly::dynamic const &args) const {
351 if (delegate_ != nullptr) {
352 delegate_->uiManagerDidDispatchCommand(shadowNode, commandName, args);
353 }
354 }
355
setNativeProps_DEPRECATED(ShadowNode::Shared const & shadowNode,RawProps const & rawProps) const356 void UIManager::setNativeProps_DEPRECATED(
357 ShadowNode::Shared const &shadowNode,
358 RawProps const &rawProps) const {
359 auto &family = shadowNode->getFamily();
360 if (family.nativeProps_DEPRECATED) {
361 // Values in `rawProps` patch (take precedence over)
362 // `nativeProps_DEPRECATED`. For example, if both `nativeProps_DEPRECATED`
363 // and `rawProps` contain key 'A'. Value from `rawProps` overrides what was
364 // previously in `nativeProps_DEPRECATED`.
365 family.nativeProps_DEPRECATED =
366 std::make_unique<folly::dynamic>(mergeDynamicProps(
367 *family.nativeProps_DEPRECATED, (folly::dynamic)rawProps));
368 } else {
369 family.nativeProps_DEPRECATED =
370 std::make_unique<folly::dynamic>((folly::dynamic)rawProps);
371 }
372
373 shadowTreeRegistry_.visit(
374 family.getSurfaceId(), [&](ShadowTree const &shadowTree) {
375 shadowTree.commit(
376 [&](RootShadowNode const &oldRootShadowNode) {
377 auto rootNode = oldRootShadowNode.cloneTree(
378 family, [&](ShadowNode const &oldShadowNode) {
379 auto &componentDescriptor =
380 componentDescriptorRegistry_->at(
381 shadowNode->getComponentHandle());
382 PropsParserContext propsParserContext{
383 family.getSurfaceId(), *contextContainer_.get()};
384 auto props = componentDescriptor.cloneProps(
385 propsParserContext,
386 getNewestCloneOfShadowNode(*shadowNode)->getProps(),
387 rawProps);
388
389 return oldShadowNode.clone({/* .props = */ props});
390 });
391
392 return std::static_pointer_cast<RootShadowNode>(rootNode);
393 },
394 {/* default commit options */});
395 });
396 }
397
sendAccessibilityEvent(const ShadowNode::Shared & shadowNode,std::string const & eventType)398 void UIManager::sendAccessibilityEvent(
399 const ShadowNode::Shared &shadowNode,
400 std::string const &eventType) {
401 if (delegate_ != nullptr) {
402 delegate_->uiManagerDidSendAccessibilityEvent(shadowNode, eventType);
403 }
404 }
405
configureNextLayoutAnimation(jsi::Runtime & runtime,RawValue const & config,jsi::Value const & successCallback,jsi::Value const & failureCallback) const406 void UIManager::configureNextLayoutAnimation(
407 jsi::Runtime &runtime,
408 RawValue const &config,
409 jsi::Value const &successCallback,
410 jsi::Value const &failureCallback) const {
411 if (animationDelegate_ != nullptr) {
412 animationDelegate_->uiManagerDidConfigureNextLayoutAnimation(
413 runtime,
414 config,
415 std::move(successCallback),
416 std::move(failureCallback));
417 }
418 }
419
findShadowNodeByTagRecursively(ShadowNode::Shared parentShadowNode,Tag tag)420 static ShadowNode::Shared findShadowNodeByTagRecursively(
421 ShadowNode::Shared parentShadowNode,
422 Tag tag) {
423 if (parentShadowNode->getTag() == tag) {
424 return parentShadowNode;
425 }
426
427 for (ShadowNode::Shared const &shadowNode : parentShadowNode->getChildren()) {
428 auto result = findShadowNodeByTagRecursively(shadowNode, tag);
429 if (result) {
430 return result;
431 }
432 }
433
434 return nullptr;
435 }
436
findShadowNodeByTag_DEPRECATED(Tag tag) const437 ShadowNode::Shared UIManager::findShadowNodeByTag_DEPRECATED(Tag tag) const {
438 auto shadowNode = ShadowNode::Shared{};
439
440 shadowTreeRegistry_.enumerate([&](ShadowTree const &shadowTree, bool &stop) {
441 RootShadowNode const *rootShadowNode;
442 // The public interface of `ShadowTree` discourages accessing a stored
443 // pointer to a root node because of the possible data race.
444 // To work around this, we ask for a commit and immediately cancel it
445 // returning `nullptr` instead of a new shadow tree.
446 // We don't want to add a way to access a stored pointer to a root node
447 // because this `findShadowNodeByTag` is deprecated. It is only added
448 // to make migration to the new architecture easier.
449 shadowTree.tryCommit(
450 [&](RootShadowNode const &oldRootShadowNode) {
451 rootShadowNode = &oldRootShadowNode;
452 return nullptr;
453 },
454 {/* default commit options */});
455
456 if (rootShadowNode != nullptr) {
457 auto const &children = rootShadowNode->getChildren();
458 if (!children.empty()) {
459 auto const &child = children.front();
460 shadowNode = findShadowNodeByTagRecursively(child, tag);
461 if (shadowNode) {
462 stop = true;
463 }
464 }
465 }
466 });
467
468 return shadowNode;
469 }
470
setComponentDescriptorRegistry(const SharedComponentDescriptorRegistry & componentDescriptorRegistry)471 void UIManager::setComponentDescriptorRegistry(
472 const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {
473 componentDescriptorRegistry_ = componentDescriptorRegistry;
474 }
475
setDelegate(UIManagerDelegate * delegate)476 void UIManager::setDelegate(UIManagerDelegate *delegate) {
477 delegate_ = delegate;
478 }
479
getDelegate()480 UIManagerDelegate *UIManager::getDelegate() {
481 return delegate_;
482 }
483
visitBinding(std::function<void (UIManagerBinding const & uiManagerBinding)> const & callback,jsi::Runtime & runtime) const484 void UIManager::visitBinding(
485 std::function<void(UIManagerBinding const &uiManagerBinding)> const
486 &callback,
487 jsi::Runtime &runtime) const {
488 auto uiManagerBinding = UIManagerBinding::getBinding(runtime);
489 if (uiManagerBinding) {
490 callback(*uiManagerBinding);
491 }
492 }
493
getShadowTreeRegistry() const494 ShadowTreeRegistry const &UIManager::getShadowTreeRegistry() const {
495 return shadowTreeRegistry_;
496 }
497
registerCommitHook(UIManagerCommitHook const & commitHook) const498 void UIManager::registerCommitHook(
499 UIManagerCommitHook const &commitHook) const {
500 std::unique_lock lock(commitHookMutex_);
501 ABI49_0_0React_native_assert(
502 std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook) ==
503 commitHooks_.end());
504 commitHook.commitHookWasRegistered(*this);
505 commitHooks_.push_back(&commitHook);
506 }
507
unregisterCommitHook(UIManagerCommitHook const & commitHook) const508 void UIManager::unregisterCommitHook(
509 UIManagerCommitHook const &commitHook) const {
510 std::unique_lock lock(commitHookMutex_);
511 auto iterator =
512 std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook);
513 ABI49_0_0React_native_assert(iterator != commitHooks_.end());
514 commitHooks_.erase(iterator);
515 commitHook.commitHookWasUnregistered(*this);
516 }
517
518 #pragma mark - ShadowTreeDelegate
519
shadowTreeWillCommit(ShadowTree const & shadowTree,RootShadowNode::Shared const & oldRootShadowNode,RootShadowNode::Unshared const & newRootShadowNode) const520 RootShadowNode::Unshared UIManager::shadowTreeWillCommit(
521 ShadowTree const &shadowTree,
522 RootShadowNode::Shared const &oldRootShadowNode,
523 RootShadowNode::Unshared const &newRootShadowNode) const {
524 std::shared_lock lock(commitHookMutex_);
525
526 auto resultRootShadowNode = newRootShadowNode;
527 for (auto const *commitHook : commitHooks_) {
528 resultRootShadowNode = commitHook->shadowTreeWillCommit(
529 shadowTree, oldRootShadowNode, resultRootShadowNode);
530 }
531
532 return resultRootShadowNode;
533 }
534
shadowTreeDidFinishTransaction(MountingCoordinator::Shared mountingCoordinator,bool mountSynchronously) const535 void UIManager::shadowTreeDidFinishTransaction(
536 MountingCoordinator::Shared mountingCoordinator,
537 bool mountSynchronously) const {
538 SystraceSection s("UIManager::shadowTreeDidFinishTransaction");
539
540 if (delegate_ != nullptr) {
541 delegate_->uiManagerDidFinishTransaction(
542 std::move(mountingCoordinator), mountSynchronously);
543 }
544 }
545
546 #pragma mark - UIManagerAnimationDelegate
547
setAnimationDelegate(UIManagerAnimationDelegate * delegate)548 void UIManager::setAnimationDelegate(UIManagerAnimationDelegate *delegate) {
549 animationDelegate_ = delegate;
550 }
551
stopSurfaceForAnimationDelegate(SurfaceId surfaceId) const552 void UIManager::stopSurfaceForAnimationDelegate(SurfaceId surfaceId) const {
553 if (animationDelegate_ != nullptr) {
554 animationDelegate_->stopSurface(surfaceId);
555 }
556 }
557
animationTick() const558 void UIManager::animationTick() const {
559 if (animationDelegate_ != nullptr &&
560 animationDelegate_->shouldAnimateFrame()) {
561 shadowTreeRegistry_.enumerate([](ShadowTree const &shadowTree, bool &) {
562 shadowTree.notifyDelegatesOfUpdates();
563 });
564 }
565 }
566
567 } // namespace ABI49_0_0facebook::ABI49_0_0React
568