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