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_0LayoutAnimationKeyFrameManager.h"
9 
10 #include <algorithm>
11 #include <sstream>
12 #include <utility>
13 
14 #include <ABI49_0_0React/debug/ABI49_0_0flags.h>
15 #include <ABI49_0_0React/debug/ABI49_0_0React_native_assert.h>
16 
17 #include <ABI49_0_0React/renderer/animations/ABI49_0_0conversions.h>
18 #include <ABI49_0_0React/renderer/animations/ABI49_0_0utils.h>
19 #include <ABI49_0_0React/renderer/componentregistry/ABI49_0_0ComponentDescriptorFactory.h>
20 #include <ABI49_0_0React/ABI49_0_0renderer/components/image/ImageProps.h>
21 #include <ABI49_0_0React/ABI49_0_0renderer/components/view/ViewProps.h>
22 #include <ABI49_0_0React/renderer/core/ABI49_0_0ComponentDescriptor.h>
23 #include <ABI49_0_0React/renderer/core/ABI49_0_0LayoutMetrics.h>
24 #include <ABI49_0_0React/renderer/core/ABI49_0_0Props.h>
25 #include <ABI49_0_0React/renderer/core/ABI49_0_0PropsParserContext.h>
26 #include <ABI49_0_0React/renderer/core/ABI49_0_0RawValue.h>
27 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0MountingCoordinator.h>
28 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0ShadowView.h>
29 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0ShadowViewMutation.h>
30 
31 #include <glog/logging.h>
32 
33 namespace ABI49_0_0facebook::ABI49_0_0React {
34 
35 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
GetMutationInstructionString(ShadowViewMutation const & mutation)36 static std::string GetMutationInstructionString(
37     ShadowViewMutation const &mutation) {
38   Tag tag = mutation.type == ShadowViewMutation::Type::Insert ||
39           mutation.type == ShadowViewMutation::Type::Create
40       ? mutation.newChildShadowView.tag
41       : mutation.oldChildShadowView.tag;
42   return getDebugName(mutation) + " [" + std::to_string(tag) + "]->[" +
43       std::to_string(mutation.parentShadowView.tag) + "] @" +
44       std::to_string(mutation.index);
45 }
46 
PrintMutationInstruction(std::string message,ShadowViewMutation const & mutation)47 void PrintMutationInstruction(
48     std::string message,
49     ShadowViewMutation const &mutation) {
50   [&](std::ostream &stream) -> std::ostream & {
51     stream << message
52            << " Mutation: " << GetMutationInstructionString(mutation);
53     if (mutation.oldChildShadowView.tag != 0) {
54       stream << " old hash: ##"
55              << std::hash<ShadowView>{}(mutation.oldChildShadowView);
56     }
57     if (mutation.newChildShadowView.tag != 0) {
58       stream << " new hash: ##"
59              << std::hash<ShadowView>{}(mutation.newChildShadowView);
60     }
61     return stream;
62   }(LOG(ERROR));
63 }
64 
PrintMutationInstructionRelative(std::string message,ShadowViewMutation const & mutation,ShadowViewMutation const & relativeMutation)65 void PrintMutationInstructionRelative(
66     std::string message,
67     ShadowViewMutation const &mutation,
68     ShadowViewMutation const &relativeMutation) {
69   LOG(ERROR) << message
70              << " Mutation: " << GetMutationInstructionString(mutation)
71              << " RelativeMutation: "
72              << GetMutationInstructionString(relativeMutation);
73 }
74 #endif
75 
76 static inline Float
interpolateFloats(Float coefficient,Float oldValue,Float newValue)77 interpolateFloats(Float coefficient, Float oldValue, Float newValue) {
78   return oldValue + (newValue - oldValue) * coefficient;
79 }
80 
81 #pragma mark -
82 
LayoutAnimationKeyFrameManager(RuntimeExecutor runtimeExecutor,ContextContainer::Shared & contextContainer,LayoutAnimationStatusDelegate * delegate)83 LayoutAnimationKeyFrameManager::LayoutAnimationKeyFrameManager(
84     RuntimeExecutor runtimeExecutor,
85     ContextContainer::Shared &contextContainer,
86     LayoutAnimationStatusDelegate *delegate)
87     : runtimeExecutor_(std::move(runtimeExecutor)),
88       contextContainer_(contextContainer),
89       layoutAnimationStatusDelegate_(delegate),
90       now_([]() {
91         return std::chrono::duration_cast<std::chrono::milliseconds>(
92                    std::chrono::high_resolution_clock::now().time_since_epoch())
93             .count();
94       }) {}
95 
96 #pragma mark UIManagerAnimationDelegate methods
97 
98 /**
99  * Globally configure next LayoutAnimation.
100  * This is guaranteed to be called only on the JS thread.
101  */
uiManagerDidConfigureNextLayoutAnimation(jsi::Runtime & runtime,RawValue const & config,const jsi::Value & successCallbackValue,const jsi::Value & failureCallbackValue) const102 void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation(
103     jsi::Runtime &runtime,
104     RawValue const &config,
105     const jsi::Value &successCallbackValue,
106     const jsi::Value &failureCallbackValue) const {
107   bool hasSuccessCallback = successCallbackValue.isObject() &&
108       successCallbackValue.getObject(runtime).isFunction(runtime);
109   bool hasFailureCallback = failureCallbackValue.isObject() &&
110       failureCallbackValue.getObject(runtime).isFunction(runtime);
111   LayoutAnimationCallbackWrapper successCallback = hasSuccessCallback
112       ? LayoutAnimationCallbackWrapper(
113             successCallbackValue.getObject(runtime).getFunction(runtime))
114       : LayoutAnimationCallbackWrapper();
115   LayoutAnimationCallbackWrapper failureCallback = hasFailureCallback
116       ? LayoutAnimationCallbackWrapper(
117             failureCallbackValue.getObject(runtime).getFunction(runtime))
118       : LayoutAnimationCallbackWrapper();
119 
120   auto layoutAnimationConfig =
121       parseLayoutAnimationConfig((folly::dynamic)config);
122 
123   if (layoutAnimationConfig) {
124     std::lock_guard<std::mutex> lock(currentAnimationMutex_);
125 
126     uiManagerDidConfigureNextLayoutAnimation(LayoutAnimation{
127         -1,
128         0,
129         false,
130         *layoutAnimationConfig,
131         successCallback,
132         failureCallback,
133         {}});
134   } else {
135     LOG(ERROR) << "Parsing LayoutAnimationConfig failed: "
136                << (folly::dynamic)config;
137 
138     callCallback(failureCallback);
139   }
140 }
141 
setComponentDescriptorRegistry(const SharedComponentDescriptorRegistry & componentDescriptorRegistry)142 void LayoutAnimationKeyFrameManager::setComponentDescriptorRegistry(
143     const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {
144   componentDescriptorRegistry_ = componentDescriptorRegistry;
145 }
146 
setReduceDeleteCreateMutation(const bool reduceDeleteCreateMutation)147 void LayoutAnimationKeyFrameManager::setReduceDeleteCreateMutation(
148     const bool reduceDeleteCreateMutation) {
149   reduceDeleteCreateMutation_ = reduceDeleteCreateMutation;
150 }
151 
shouldAnimateFrame() const152 bool LayoutAnimationKeyFrameManager::shouldAnimateFrame() const {
153   std::lock_guard<std::mutex> lock(currentAnimationMutex_);
154   return currentAnimation_ || !inflightAnimations_.empty();
155 }
156 
stopSurface(SurfaceId surfaceId)157 void LayoutAnimationKeyFrameManager::stopSurface(SurfaceId surfaceId) {
158   std::lock_guard<std::mutex> lock(surfaceIdsToStopMutex_);
159   surfaceIdsToStop_.insert(surfaceId);
160 }
161 
162 #pragma mark - MountingOverrideDelegate methods
163 
shouldOverridePullTransaction() const164 bool LayoutAnimationKeyFrameManager::shouldOverridePullTransaction() const {
165   return shouldAnimateFrame();
166 }
167 
168 std::optional<MountingTransaction>
pullTransaction(SurfaceId surfaceId,MountingTransaction::Number transactionNumber,TransactionTelemetry const & telemetry,ShadowViewMutationList mutations) const169 LayoutAnimationKeyFrameManager::pullTransaction(
170     SurfaceId surfaceId,
171     MountingTransaction::Number transactionNumber,
172     TransactionTelemetry const &telemetry,
173     ShadowViewMutationList mutations) const {
174   // Current time in milliseconds
175   uint64_t now = now_();
176 
177   bool inflightAnimationsExistInitially = !inflightAnimations_.empty();
178   deleteAnimationsForStoppedSurfaces();
179 
180   if (!mutations.empty()) {
181 #ifdef ABI49_0_0RN_SHADOW_TREE_INTROSPECTION
182     {
183       std::stringstream ss(getDebugDescription(mutations, {}));
184       std::string to;
185       while (std::getline(ss, to, '\n')) {
186         LOG(ERROR)
187             << "LayoutAnimationKeyFrameManager.cpp: got mutation list: Line: "
188             << to;
189       }
190     };
191 #endif
192 
193       // DEBUG ONLY: list existing inflight animations
194 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
195     LOG(ERROR) << "BEGINNING DISPLAYING ONGOING inflightAnimations_!";
196     int i = 0;
197     int j = 0;
198     for (auto const &inflightAnimation : inflightAnimations_) {
199       i++;
200       j = 0;
201       if (inflightAnimation.completed) {
202         continue;
203       }
204       for (auto &keyframe : inflightAnimation.keyFrames) {
205         j++;
206         if (keyframe.invalidated) {
207           continue;
208         }
209         for (const auto &finalMutationForKeyFrame :
210              keyframe.finalMutationsForKeyFrame) {
211           if (finalMutationForKeyFrame.mutatedViewIsVirtual()) {
212             std::string msg = "Animation " + std::to_string(i) + " keyframe " +
213                 std::to_string(j) + ": Final Animation";
214             PrintMutationInstruction(msg, finalMutationForKeyFrame);
215           } else {
216             LOG(ERROR) << "Animation " << i << " keyframe " << j
217                        << ": on tag: [" << keyframe.viewStart.tag << "]";
218           }
219         }
220       }
221     }
222     LOG(ERROR) << "BEGINNING DONE DISPLAYING ONGOING inflightAnimations_!";
223 #endif
224 
225     PropsParserContext propsParserContext{surfaceId, *contextContainer_};
226 
227     // What to do if we detect a conflict? Get current value and make
228     // that the baseline of the next animation. Scale the remaining time
229     // in the animation
230     // Types of conflicts and how we handle them:
231     // Update -> update: remove the previous update, make it the baseline of the
232     // next update (with current progress) Update -> remove: same, with final
233     // mutation being a remove Insert -> update: treat as update->update Insert
234     // -> remove: same, as update->remove Remove -> update/insert: not possible
235     // We just collect pairs here of <Mutation, AnimationConfig> and delete them
236     // from active animations. If another animation is queued up from the
237     // current mutations then these deleted mutations will serve as the baseline
238     // for the next animation. If not, the current mutations are executed
239     // immediately without issues.
240     std::vector<AnimationKeyFrame> conflictingAnimations{};
241     getAndEraseConflictingAnimations(
242         surfaceId, mutations, conflictingAnimations);
243 
244     // Are we animating this list of mutations?
245     std::optional<LayoutAnimation> currentAnimation{};
246     {
247       std::lock_guard<std::mutex> lock(currentAnimationMutex_);
248       if (currentAnimation_) {
249         currentAnimation = std::move(currentAnimation_);
250         currentAnimation_.reset();
251       }
252     }
253 
254     if (currentAnimation.has_value()) {
255       LayoutAnimation animation = std::move(currentAnimation).value();
256       animation.surfaceId = surfaceId;
257       animation.startTime = now;
258 
259       // Pre-process list to:
260       //   Catch remove+reinsert (reorders)
261       //   Catch delete+create (reparenting) (this should be optimized away at
262       //   the diffing level eventually?)
263       // TODO: to prevent this step we could tag Remove/Insert mutations as
264       // being moves on the Differ level, since we know that there? We could use
265       // TinyMap here, but it's not exposed by Differentiator (yet).
266       butter::set<Tag> insertedTags;
267       butter::set<Tag> deletedTags;
268       butter::set<Tag> reparentedTags; // tags that are deleted and recreated
269       std::unordered_map<Tag, ShadowViewMutation> movedTags;
270       for (const auto &mutation : mutations) {
271         if (mutation.type == ShadowViewMutation::Type::Insert) {
272           insertedTags.insert(mutation.newChildShadowView.tag);
273         }
274         if (mutation.type == ShadowViewMutation::Type::Delete) {
275           deletedTags.insert(mutation.oldChildShadowView.tag);
276         }
277         if (mutation.type == ShadowViewMutation::Type::Create) {
278           if (deletedTags.find(mutation.newChildShadowView.tag) !=
279               deletedTags.end()) {
280             reparentedTags.insert(mutation.newChildShadowView.tag);
281           }
282         }
283       }
284 
285       // Process mutations list into operations that can be sent to platform
286       // immediately, and those that need to be animated Deletions, removals,
287       // updates are delayed and animated. Creations and insertions are sent to
288       // platform and then "animated in" with opacity updates. Upon completion,
289       // removals and deletions are sent to platform
290       ShadowViewMutation::List immediateMutations;
291 
292       // Remove operations that are actually moves should be copied to
293       // "immediate mutations". The corresponding "insert" will also be executed
294       // immediately and animated as an update.
295       std::vector<AnimationKeyFrame> keyFramesToAnimate;
296       auto const layoutAnimationConfig = animation.layoutAnimationConfig;
297       for (auto const &mutation : mutations) {
298         if (mutation.type == ShadowViewMutation::Type::RemoveDeleteTree) {
299           continue;
300         }
301 
302         ShadowView baselineShadowView =
303             (mutation.type == ShadowViewMutation::Type::Delete ||
304                      mutation.type == ShadowViewMutation::Type::Remove ||
305                      mutation.type == ShadowViewMutation::Type::Update
306                  ? mutation.oldChildShadowView
307                  : mutation.newChildShadowView);
308         ABI49_0_0React_native_assert(baselineShadowView.tag > 0);
309         bool haveComponentDescriptor =
310             hasComponentDescriptorForShadowView(baselineShadowView);
311 
312         // Immediately execute any mutations on a root node
313         if (baselineShadowView.traits.check(
314                 ShadowNodeTraits::Trait::RootNodeKind)) {
315           immediateMutations.push_back(mutation);
316           continue;
317         }
318 
319         std::optional<ShadowViewMutation> executeMutationImmediately{};
320 
321         bool isRemoveReinserted =
322             mutation.type == ShadowViewMutation::Type::Remove &&
323             insertedTags.find(mutation.oldChildShadowView.tag) !=
324                 insertedTags.end();
325 
326         // Reparenting can result in a node being removed, inserted (moved) and
327         // also deleted and created in the same frame, with the same props etc.
328         // This should eventually be optimized out of the diffing algorithm, but
329         // for now we detect reparenting and prevent the corresponding
330         // Delete/Create instructions from being animated.
331         bool isReparented =
332             reparentedTags.find(baselineShadowView.tag) != reparentedTags.end();
333 
334         if (isRemoveReinserted) {
335           movedTags.insert({mutation.oldChildShadowView.tag, mutation});
336         }
337 
338         // Inserts that follow a "remove" of the same tag should be treated as
339         // an update (move) animation.
340         bool wasInsertedTagRemoved = false;
341         auto movedIt = movedTags.end();
342         if (mutation.type == ShadowViewMutation::Type::Insert) {
343           // If this is a move, we actually don't want to copy this insert
344           // instruction to animated instructions - we want to
345           // generate an Update mutation for Remove+Insert pairs to animate
346           // the layout.
347           // The corresponding Remove and Insert instructions will instead
348           // be treated as "immediate" instructions.
349           movedIt = movedTags.find(mutation.newChildShadowView.tag);
350           wasInsertedTagRemoved = movedIt != movedTags.end();
351         }
352 
353         auto const &mutationConfig =
354             (mutation.type == ShadowViewMutation::Type::Delete ||
355                      (mutation.type == ShadowViewMutation::Type::Remove &&
356                       !wasInsertedTagRemoved)
357                  ? layoutAnimationConfig.deleteConfig
358                  : (mutation.type == ShadowViewMutation::Type::Insert &&
359                             !wasInsertedTagRemoved
360                         ? layoutAnimationConfig.createConfig
361                         : layoutAnimationConfig.updateConfig));
362         bool haveConfiguration =
363             mutationConfig.animationType != AnimationType::None;
364 
365         // Creates and inserts should also be executed immediately.
366         // Mutations that would otherwise be animated, but have no
367         // configuration, are also executed immediately.
368         if (isRemoveReinserted || !haveConfiguration || isReparented ||
369             mutation.type == ShadowViewMutation::Type::Create ||
370             mutation.type == ShadowViewMutation::Type::Insert) {
371           executeMutationImmediately = mutation;
372 
373           // It is possible, especially in the case of "moves", that we have a
374           // sequence of operations like:
375           // UPDATE X
376           // REMOVE X
377           // INSERT X
378           // In these cases, we will have queued up an animation for the UPDATE
379           // and delayed its execution; the REMOVE and INSERT will be executed
380           // first; and then the UPDATE will be animating to/from ShadowViews
381           // that are out-of-sync with what's on the mounting layer. Thus, for
382           // any UPDATE animations already queued up for this tag, we adjust the
383           // "previous" ShadowView.
384           if (mutation.type == ShadowViewMutation::Type::Insert) {
385             for (auto &keyframe : keyFramesToAnimate) {
386               if (keyframe.tag == baselineShadowView.tag) {
387                 // If there's already an animation queued up, followed by this
388                 // Insert, it *must* be an Update mutation animation. Other
389                 // sequences should not be possible.
390                 ABI49_0_0React_native_assert(
391                     keyframe.type == AnimationConfigurationType::Update);
392 
393                 // The mutation is an "insert", so it must have a
394                 // "newChildShadowView"
395                 ABI49_0_0React_native_assert(mutation.newChildShadowView.tag > 0);
396 
397                 // Those asserts don't run in prod. If there's some edge-case
398                 // that we haven't caught yet, we'd crash in debug; make sure we
399                 // don't mutate the prevView in prod.
400                 if (keyframe.type == AnimationConfigurationType::Update &&
401                     mutation.newChildShadowView.tag > 0) {
402                   keyframe.viewPrev = mutation.newChildShadowView;
403                 }
404               }
405             }
406           } else if (mutation.type == ShadowViewMutation::Type::Remove) {
407             for (auto &keyframe : keyFramesToAnimate) {
408               if (keyframe.tag == baselineShadowView.tag) {
409                 // If there's already an animation queued up, followed by this
410                 // Insert, it *must* be an Update mutation animation. Other
411                 // sequences should not be possible.
412                 ABI49_0_0React_native_assert(
413                     keyframe.type == AnimationConfigurationType::Update);
414 
415                 // The mutation is a "remove", so it must have a
416                 // "oldChildShadowView"
417                 ABI49_0_0React_native_assert(mutation.oldChildShadowView.tag > 0);
418 
419                 // Those asserts don't run in prod. If there's some edge-case
420                 // that we haven't caught yet, we'd crash in debug; make sure we
421                 // don't mutate the prevView in prod.
422                 // Since normally the UPDATE would have been executed first and
423                 // now it's deferred, we need to change the `oldChildShadowView`
424                 // that is being referenced by the REMOVE mutation.
425                 if (keyframe.type == AnimationConfigurationType::Update &&
426                     mutation.oldChildShadowView.tag > 0) {
427                   executeMutationImmediately =
428                       ShadowViewMutation::RemoveMutation(
429                           mutation.parentShadowView,
430                           keyframe.viewPrev,
431                           mutation.index);
432                 }
433               }
434             }
435           }
436         }
437 
438         // Deletes, non-move inserts, updates get animated
439         if (!wasInsertedTagRemoved && !isRemoveReinserted && !isReparented &&
440             haveConfiguration &&
441             mutation.type != ShadowViewMutation::Type::Create) {
442           ShadowView viewStart = ShadowView(
443               mutation.type == ShadowViewMutation::Type::Insert
444                   ? mutation.newChildShadowView
445                   : mutation.oldChildShadowView);
446           ABI49_0_0React_native_assert(viewStart.tag > 0);
447           ShadowView viewFinal = ShadowView(
448               mutation.type == ShadowViewMutation::Type::Update
449                   ? mutation.newChildShadowView
450                   : viewStart);
451           ABI49_0_0React_native_assert(viewFinal.tag > 0);
452           ShadowView parent = mutation.parentShadowView;
453           ABI49_0_0React_native_assert(
454               parent.tag > 0 ||
455               mutation.type == ShadowViewMutation::Type::Update ||
456               mutation.type == ShadowViewMutation::Type::Delete);
457           Tag tag = viewStart.tag;
458 
459           AnimationKeyFrame keyFrame{};
460           if (mutation.type == ShadowViewMutation::Type::Insert) {
461             if (mutationConfig.animationProperty ==
462                     AnimationProperty::Opacity &&
463                 haveComponentDescriptor) {
464               auto props =
465                   getComponentDescriptorForShadowView(baselineShadowView)
466                       .cloneProps(propsParserContext, viewStart.props, {});
467 
468               if (baselineShadowView.traits.check(
469                       ShadowNodeTraits::Trait::ViewKind)) {
470                 auto const &viewProps =
471                     *std::static_pointer_cast<ViewProps const>(props);
472                 const_cast<ViewProps &>(viewProps).opacity = 0;
473               }
474 
475               ABI49_0_0React_native_assert(props != nullptr);
476               if (props != nullptr) {
477                 viewStart.props = props;
478               }
479             }
480             bool isScaleX =
481                 mutationConfig.animationProperty == AnimationProperty::ScaleX ||
482                 mutationConfig.animationProperty == AnimationProperty::ScaleXY;
483             bool isScaleY =
484                 mutationConfig.animationProperty == AnimationProperty::ScaleY ||
485                 mutationConfig.animationProperty == AnimationProperty::ScaleXY;
486             if ((isScaleX || isScaleY) && haveComponentDescriptor) {
487               auto props =
488                   getComponentDescriptorForShadowView(baselineShadowView)
489                       .cloneProps(propsParserContext, viewStart.props, {});
490               if (baselineShadowView.traits.check(
491                       ShadowNodeTraits::Trait::ViewKind)) {
492                 auto const &viewProps =
493                     *std::static_pointer_cast<ViewProps const>(props);
494                 const_cast<ViewProps &>(viewProps).transform =
495                     Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1);
496               }
497 
498               ABI49_0_0React_native_assert(props != nullptr);
499               if (props != nullptr) {
500                 viewStart.props = props;
501               }
502             }
503 
504             PrintMutationInstruction(
505                 "Setting up animation KeyFrame for INSERT mutation (Create animation)",
506                 mutation);
507 
508             keyFrame = AnimationKeyFrame{
509                 /* .finalMutationsForKeyFrame = */ {},
510                 /* .type = */ AnimationConfigurationType::Create,
511                 /* .tag = */ tag,
512                 /* .parentView = */ parent,
513                 /* .viewStart = */ viewStart,
514                 /* .viewEnd = */ viewFinal,
515                 /* .viewPrev = */ baselineShadowView,
516                 /* .initialProgress = */ 0};
517           } else if (mutation.type == ShadowViewMutation::Type::Delete) {
518 // This is just for assertion purposes.
519 // The NDEBUG check here is to satisfy the compiler in certain environments
520 // complaining about correspondingRemoveIt being unused.
521 #ifdef ABI49_0_0REACT_NATIVE_DEBUG
522 #ifndef NDEBUG
523 // This block is temporarily disabled to fix some internal builds.
524 // In some build configurations, we get a compiler error that
525 // `correspondingRemoveIt` is unused.
526 /*            Tag deleteTag = mutation.oldChildShadowView.tag;
527             auto correspondingRemoveIt = std::find_if(
528                 mutations.begin(),
529                 mutations.end(),
530                 [&deleteTag](auto &mutation) {
531                   return mutation.type == ShadowViewMutation::Type::Remove &&
532                       mutation.oldChildShadowView.tag == deleteTag;
533                 });
534             ABI49_0_0React_native_assert(correspondingRemoveIt != mutations.end());
535 */
536 #endif
537 #endif
538             continue;
539           } else if (mutation.type == ShadowViewMutation::Type::Update) {
540             viewFinal = ShadowView(mutation.newChildShadowView);
541 
542             PrintMutationInstruction(
543                 "Setting up animation KeyFrame for UPDATE mutation (Update animation)",
544                 mutation);
545 
546             keyFrame = AnimationKeyFrame{
547                 /* .finalMutationsForKeyFrame = */ {mutation},
548                 /* .type = */ AnimationConfigurationType::Update,
549                 /* .tag = */ tag,
550                 /* .parentView = */ parent,
551                 /* .viewStart = */ viewStart,
552                 /* .viewEnd = */ viewFinal,
553                 /* .viewPrev = */ baselineShadowView,
554                 /* .initialProgress = */ 0};
555           } else {
556             // This should just be "Remove" instructions that are not animated
557             // (either this is a "move", or there's a corresponding "Delete"
558             // that is animated).
559             ABI49_0_0React_native_assert(
560                 mutation.type == ShadowViewMutation::Type::Remove);
561 
562             Tag removeTag = mutation.oldChildShadowView.tag;
563             auto correspondingInsertIt = std::find_if(
564                 mutations.begin(),
565                 mutations.end(),
566                 [&removeTag](auto &mutation) {
567                   return mutation.type == ShadowViewMutation::Type::Insert &&
568                       mutation.newChildShadowView.tag == removeTag;
569                 });
570             if (correspondingInsertIt == mutations.end()) {
571               // This is a REMOVE not paired with an INSERT (move), so it must
572               // be paired with a DELETE.
573               auto correspondingDeleteIt = std::find_if(
574                   mutations.begin(),
575                   mutations.end(),
576                   [&removeTag](auto &mutation) {
577                     return mutation.type == ShadowViewMutation::Type::Delete &&
578                         mutation.oldChildShadowView.tag == removeTag;
579                   });
580               ABI49_0_0React_native_assert(correspondingDeleteIt != mutations.end());
581 
582               auto deleteMutation = *correspondingDeleteIt;
583 
584               if (mutationConfig.animationProperty ==
585                       AnimationProperty::Opacity &&
586                   haveComponentDescriptor) {
587                 auto props =
588                     getComponentDescriptorForShadowView(baselineShadowView)
589                         .cloneProps(propsParserContext, viewFinal.props, {});
590 
591                 if (baselineShadowView.traits.check(
592                         ShadowNodeTraits::Trait::ViewKind)) {
593                   auto const &viewProps =
594                       *std::static_pointer_cast<ViewProps const>(props);
595                   const_cast<ViewProps &>(viewProps).opacity = 0;
596                 }
597 
598                 ABI49_0_0React_native_assert(props != nullptr);
599                 if (props != nullptr) {
600                   viewFinal.props = props;
601                 }
602               }
603               bool isScaleX = mutationConfig.animationProperty ==
604                       AnimationProperty::ScaleX ||
605                   mutationConfig.animationProperty ==
606                       AnimationProperty::ScaleXY;
607               bool isScaleY = mutationConfig.animationProperty ==
608                       AnimationProperty::ScaleY ||
609                   mutationConfig.animationProperty ==
610                       AnimationProperty::ScaleXY;
611               if ((isScaleX || isScaleY) && haveComponentDescriptor) {
612                 auto props =
613                     getComponentDescriptorForShadowView(baselineShadowView)
614                         .cloneProps(propsParserContext, viewFinal.props, {});
615 
616                 if (baselineShadowView.traits.check(
617                         ShadowNodeTraits::Trait::ViewKind)) {
618                   auto const &viewProps =
619                       *std::static_pointer_cast<ViewProps const>(props);
620                   const_cast<ViewProps &>(viewProps).transform =
621                       Transform::Scale(isScaleX ? 0 : 1, isScaleY ? 0 : 1, 1);
622                 }
623 
624                 ABI49_0_0React_native_assert(props != nullptr);
625                 if (props != nullptr) {
626                   viewFinal.props = props;
627                 }
628               }
629 
630               PrintMutationInstruction(
631                   "Setting up animation KeyFrame for REMOVE mutation (Delete animation)",
632                   mutation);
633 
634               keyFrame = AnimationKeyFrame{
635                   /* .finalMutationsForKeyFrame */ {mutation, deleteMutation},
636                   /* .type */ AnimationConfigurationType::Delete,
637                   /* .tag */ tag,
638                   /* .parentView */ parent,
639                   /* .viewStart */ viewStart,
640                   /* .viewEnd */ viewFinal,
641                   /* .viewPrev */ baselineShadowView,
642                   /* .initialProgress */ 0};
643             } else {
644               PrintMutationInstruction(
645                   "Executing Remove Immediately, due to reordering operation",
646                   mutation);
647               immediateMutations.push_back(mutation);
648               continue;
649             }
650           }
651 
652           // Handle conflicting animations
653           for (auto &conflictingKeyFrame : conflictingAnimations) {
654             auto const &conflictingMutationBaselineShadowView =
655                 conflictingKeyFrame.viewStart;
656 
657             // We've found a conflict.
658             if (conflictingMutationBaselineShadowView.tag == tag) {
659               conflictingKeyFrame.generateFinalSyntheticMutations = false;
660 
661               // Do NOT update viewStart for a CREATE animation.
662               if (keyFrame.type == AnimationConfigurationType::Create) {
663                 break;
664               }
665 
666 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
667               LOG(ERROR)
668                   << "Due to conflict, replacing 'viewStart' of animated keyframe: ["
669                   << conflictingKeyFrame.viewPrev.tag << "] with ##"
670                   << std::hash<ShadowView>{}(conflictingKeyFrame.viewPrev);
671 #endif
672               // Pick a Prop or layout property, depending on the current
673               // animation configuration. Figure out how much progress we've
674               // already made in the current animation, and start the animation
675               // from this point.
676               keyFrame.viewPrev = conflictingKeyFrame.viewPrev;
677               keyFrame.viewStart = conflictingKeyFrame.viewPrev;
678               ABI49_0_0React_native_assert(keyFrame.viewStart.tag > 0);
679               keyFrame.initialProgress = 0;
680 
681               // We're guaranteed that a tag only has one animation associated
682               // with it, so we can break here. If we support multiple
683               // animations and animation curves over the same tag in the
684               // future, this will need to be modified to support that.
685               break;
686             }
687           }
688 
689 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
690           LOG(ERROR) << "Checking validity of keyframe: ["
691                      << keyFrame.viewStart.tag << "] [" << keyFrame.viewEnd.tag
692                      << "] [" << keyFrame.viewPrev.tag
693                      << "] animation type: " << (int)keyFrame.type;
694 #endif
695           ABI49_0_0React_native_assert(keyFrame.viewStart.tag > 0);
696           ABI49_0_0React_native_assert(keyFrame.viewEnd.tag > 0);
697           ABI49_0_0React_native_assert(keyFrame.viewPrev.tag > 0);
698           keyFramesToAnimate.push_back(keyFrame);
699         }
700 
701         if (executeMutationImmediately.has_value()) {
702           PrintMutationInstruction(
703               "Queue Up For Immediate Execution", *executeMutationImmediately);
704           immediateMutations.push_back(*executeMutationImmediately);
705         }
706       }
707 
708 #ifdef ABI49_0_0RN_SHADOW_TREE_INTROSPECTION
709 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
710       {
711         int idx = 0;
712         for (auto &mutation : immediateMutations) {
713           PrintMutationInstruction(
714               std::string("IMMEDIATE list: ") + std::to_string(idx) + "/" +
715                   std::to_string(immediateMutations.size()),
716               mutation);
717           idx++;
718         }
719       }
720 
721       {
722         int idx = 0;
723         for (const auto &keyframe : keyFramesToAnimate) {
724           for (const auto &finalMutationForKeyFrame :
725                keyframe.finalMutationsForKeyFrame) {
726             PrintMutationInstruction(
727                 std::string("FINAL list: ") + std::to_string(idx) + "/" +
728                     std::to_string(keyFramesToAnimate.size()),
729                 finalMutationForKeyFrame);
730           }
731           idx++;
732         }
733       }
734 #endif
735 #endif
736 
737       auto finalConflictingMutations = ShadowViewMutationList{};
738       for (auto &keyFrame : conflictingAnimations) {
739         // Special-case: if the next conflicting animation contain "delete",
740         // while the final mutation has the same tag with "create", we should
741         // remove both the delete and create as they have no effect when
742         // combined in the same frame. The Fabric mount layer assumes no such
743         // combinations in the final mutations either.
744         if (reduceDeleteCreateMutation_) {
745           for (auto itMutation = immediateMutations.begin();
746                itMutation != immediateMutations.end();) {
747             auto &mutation = *itMutation;
748             bool hasCreateMutationDeletedWithSameTag = false;
749             if (mutation.newChildShadowView.tag == keyFrame.tag &&
750                 mutation.type == ShadowViewMutation::Create) {
751               for (auto itKeyFrame = keyFrame.finalMutationsForKeyFrame.begin();
752                    itKeyFrame != keyFrame.finalMutationsForKeyFrame.end();) {
753                 auto &conflictFinalMutation = *itKeyFrame;
754                 if (conflictFinalMutation.type == ShadowViewMutation::Delete) {
755                   itKeyFrame =
756                       keyFrame.finalMutationsForKeyFrame.erase(itKeyFrame);
757                   hasCreateMutationDeletedWithSameTag = true;
758                   break;
759                 } else {
760                   itKeyFrame++;
761                 }
762               }
763             }
764 
765             if (hasCreateMutationDeletedWithSameTag) {
766               itMutation = immediateMutations.erase(itMutation);
767             } else {
768               itMutation++;
769             }
770           }
771         }
772 
773         // Special-case: if we have some (1) ongoing UPDATE animation,
774         // (2) it conflicted with a new MOVE operation (REMOVE+INSERT)
775         // without another corresponding UPDATE, we should re-queue the
776         // keyframe so that its position/props don't suddenly "jump".
777         if (keyFrame.type == AnimationConfigurationType::Update) {
778           auto movedIt = movedTags.find(keyFrame.tag);
779           if (movedIt != movedTags.end()) {
780             auto newKeyFrameForUpdate = std::find_if(
781                 keyFramesToAnimate.begin(),
782                 keyFramesToAnimate.end(),
783                 [&](auto const &newKeyFrame) {
784                   return newKeyFrame.type ==
785                       AnimationConfigurationType::Update &&
786                       newKeyFrame.tag == keyFrame.tag;
787                 });
788             if (newKeyFrameForUpdate == keyFramesToAnimate.end()) {
789               keyFrame.invalidated = false;
790 
791               // The animation will continue from the current position - we
792               // restart viewStart to make sure there are no sudden jumps
793               keyFrame.viewStart = keyFrame.viewPrev;
794 
795               // Find the insert mutation that conflicted with this update
796               for (auto &mutation : immediateMutations) {
797                 if (mutation.newChildShadowView.tag == keyFrame.tag &&
798                     (mutation.type == ShadowViewMutation::Insert ||
799                      mutation.type == ShadowViewMutation::Create)) {
800                   keyFrame.viewPrev = mutation.newChildShadowView;
801                   keyFrame.viewEnd = mutation.newChildShadowView;
802                 }
803               }
804               keyFramesToAnimate.push_back(keyFrame);
805               continue;
806             }
807           }
808         }
809 
810         // If the "final" mutation is already accounted for, by previously
811         // setting the correct "viewPrev" of the next conflicting animation, we
812         // don't want to queue up any final UPDATE mutations here.
813         bool shouldGenerateSyntheticMutations =
814             keyFrame.generateFinalSyntheticMutations;
815         auto numFinalMutations = keyFrame.finalMutationsForKeyFrame.size();
816         bool onlyMutationIsUpdate =
817             (numFinalMutations == 1 &&
818              keyFrame.finalMutationsForKeyFrame[0].type ==
819                  ShadowViewMutation::Update);
820         if (!shouldGenerateSyntheticMutations &&
821             (numFinalMutations == 0 || onlyMutationIsUpdate)) {
822           continue;
823         }
824 
825         queueFinalMutationsForCompletedKeyFrame(
826             keyFrame,
827             finalConflictingMutations,
828             true,
829             "KeyFrameManager: Finished Conflicting Keyframe");
830       }
831 
832       // Make sure that all operations execute in the proper order, since
833       // conflicting animations are not sorted in any reasonable way.
834       std::stable_sort(
835           finalConflictingMutations.begin(),
836           finalConflictingMutations.end(),
837           &shouldFirstComeBeforeSecondMutation);
838 
839       std::stable_sort(
840           immediateMutations.begin(),
841           immediateMutations.end(),
842           &shouldFirstComeBeforeSecondRemovesOnly);
843 
844       animation.keyFrames = keyFramesToAnimate;
845       inflightAnimations_.push_back(std::move(animation));
846 
847       // At this point, we have the following information and knowledge graph:
848       // Knowledge Graph:
849       // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed],
850       // [Delayed] already executed [FrameDelayed] -> assumes
851       // [FinalConflicting], [Delayed] already executed [FinalConflicting] ->
852       // is adjusted based on [Delayed], no dependency on [FinalConflicting],
853       // [FrameDelayed] [Delayed] -> assumes [FinalConflicting],
854       // [ImmediateMutations] not executed yet
855 
856       // Adjust [Delayed] based on [FinalConflicting]
857       // Knowledge Graph:
858       // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed],
859       // [Delayed] already executed [FrameDelayed] -> assumes
860       // [FinalConflicting], [Delayed] already executed [FinalConflicting] ->
861       // is adjusted based on [Delayed], no dependency on [FinalConflicting],
862       // [FrameDelayed] [Delayed] -> adjusted for [FinalConflicting]; assumes
863       // [ImmediateMutations] not executed yet
864 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
865       LOG(ERROR) << "Adjust [Delayed] based on [FinalConflicting]";
866 #endif
867       for (auto &mutation : finalConflictingMutations) {
868         if (mutation.type == ShadowViewMutation::Type::Insert ||
869             mutation.type == ShadowViewMutation::Type::Remove) {
870           adjustDelayedMutationIndicesForMutation(surfaceId, mutation, true);
871         }
872       }
873 
874       // Adjust [FrameDelayed] based on [Delayed]
875       // Knowledge Graph:
876       // [ImmediateExecutions] -> assumes [FinalConflicting], [Delayed],
877       // [FrameDelayed] already executed [FrameDelayed] -> adjusted for
878       // [Delayed]; assumes [FinalConflicting] already executed
879       // [FinalConflicting] -> is adjusted based on [Delayed], no dependency
880       // on [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for
881       // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet
882 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
883       LOG(ERROR) << "Adjust [FrameDelayed] based on [Delayed]";
884 #endif
885       for (auto &keyframe : inflightAnimations_.back().keyFrames) {
886         for (auto &finalMutation : keyframe.finalMutationsForKeyFrame) {
887           if (finalMutation.type == ShadowViewMutation::Type::Insert ||
888               finalMutation.type == ShadowViewMutation::Type::Remove) {
889             // When adjusting, skip adjusting against last animation - because
890             // all `mutation`s here come from the last animation, so we can't
891             // adjust a batch against itself.
892             adjustImmediateMutationIndicesForDelayedMutations(
893                 surfaceId, finalMutation, true);
894           }
895         }
896       }
897 
898       // Adjust [ImmediateExecutions] based on [Delayed]
899       // Knowledge Graph:
900       // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed];
901       // assumes [FinalConflicting] already executed [FrameDelayed] ->
902       // adjusted for [Delayed]; assumes [FinalConflicting] already executed
903       // [FinalConflicting] -> is adjusted based on [Delayed], no dependency
904       // on [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for
905       // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet
906       //
907       // THEN,
908       // Adjust [Delayed] based on [ImmediateExecutions] and
909       // [FinalConflicting] Knowledge Graph: [ImmediateExecutions] -> adjusted
910       // for [FrameDelayed], [Delayed]; assumes [FinalConflicting] already
911       // executed [FrameDelayed] -> adjusted for [Delayed]; assumes
912       // [FinalConflicting] already executed [FinalConflicting] -> is adjusted
913       // based on [Delayed], no dependency on [FinalConflicting],
914       // [FrameDelayed] [Delayed] -> adjusted for [FinalConflicting],
915       // [ImmediateExecutions]
916       //
917       // We do these in the same loop because each immediate execution is
918       // impacted by each delayed mutation, and also can impact each delayed
919       // mutation, and these effects compound.
920 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
921       LOG(ERROR)
922           << "Adjust each [ImmediateExecution] based on [Delayed] and [Delayed] based on each [ImmediateExecution]";
923 #endif
924       for (auto &mutation : immediateMutations) {
925         // Note: when adjusting [ImmediateExecutions] based on [FrameDelayed],
926         // we need only adjust Inserts. Since inserts are executed
927         // highest-index-first, lower indices being delayed does not impact
928         // the higher-index removals; and conversely, higher indices being
929         // delayed cannot impact lower index removal, regardless of order.
930         if (mutation.type == ShadowViewMutation::Type::Insert ||
931             mutation.type == ShadowViewMutation::Type::Remove) {
932           adjustImmediateMutationIndicesForDelayedMutations(
933               surfaceId,
934               mutation,
935               mutation.type == ShadowViewMutation::Type::Remove);
936           // Here we need to adjust both Delayed and FrameDelayed mutations.
937           // Delayed Removes can be impacted by non-delayed Inserts from the
938           // same frame.
939           adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
940         }
941       }
942 
943       // If the knowledge graph progression above is correct, it is now safe
944       // to execute finalConflictingMutations and immediateMutations in that
945       // order, and to queue the delayed animations from this frame.
946       //
947       // Execute the conflicting, delayed operations immediately. Any UPDATE
948       // operations that smoothly transition into another animation will be
949       // overridden by generated UPDATE operations at the end of the list, and
950       // we want any REMOVE or DELETE operations to execute immediately.
951       // Additionally, this should allow us to avoid performing index
952       // adjustment between this list of conflicting animations and the batch
953       // we're about to execute.
954       finalConflictingMutations.insert(
955           finalConflictingMutations.end(),
956           immediateMutations.begin(),
957           immediateMutations.end());
958       mutations = finalConflictingMutations;
959     } /* if (currentAnimation) */
960     else {
961       // If there's no "next" animation, make sure we queue up "final"
962       // operations from all ongoing, conflicting animations.
963 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
964       LOG(ERROR) << "No Animation: Queue up final conflicting animations";
965 #endif
966       ShadowViewMutationList finalMutationsForConflictingAnimations{};
967       for (auto const &keyFrame : conflictingAnimations) {
968         queueFinalMutationsForCompletedKeyFrame(
969             keyFrame,
970             finalMutationsForConflictingAnimations,
971             true,
972             "Conflict with non-animated mutation");
973       }
974 
975       // Make sure that all operations execute in the proper order.
976       // REMOVE operations with highest indices must operate first.
977       std::stable_sort(
978           finalMutationsForConflictingAnimations.begin(),
979           finalMutationsForConflictingAnimations.end(),
980           &shouldFirstComeBeforeSecondMutation);
981 
982 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
983       LOG(ERROR)
984           << "No Animation: Adjust delayed mutations based on all finalMutationsForConflictingAnimations";
985 #endif
986       for (auto const &mutation : finalMutationsForConflictingAnimations) {
987         if (mutation.type == ShadowViewMutation::Type::Remove ||
988             mutation.type == ShadowViewMutation::Type::Insert) {
989           adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
990         }
991       }
992 
993       // The ShadowTree layer doesn't realize that certain operations have
994       // been delayed, so we must adjust all Remove and Insert operations
995       // based on what else has been deferred, whether we are executing this
996       // immediately or later.
997 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
998       LOG(ERROR)
999           << "No Animation: Adjust mutations based on remaining delayed mutations / adjust delayed, based on each";
1000 #endif
1001       for (auto &mutation : mutations) {
1002         if (mutation.type == ShadowViewMutation::Type::Remove ||
1003             mutation.type == ShadowViewMutation::Type::Insert) {
1004           adjustImmediateMutationIndicesForDelayedMutations(
1005               surfaceId, mutation);
1006           adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
1007         }
1008       }
1009 
1010       // Append mutations to this list and swap - so that the final
1011       // conflicting mutations happen before any other mutations
1012       finalMutationsForConflictingAnimations.insert(
1013           finalMutationsForConflictingAnimations.end(),
1014           mutations.begin(),
1015           mutations.end());
1016       mutations = finalMutationsForConflictingAnimations;
1017     }
1018   } // if (mutations)
1019 
1020   // We never commit a different root or modify anything -
1021   // we just send additional mutations to the mounting layer until the
1022   // animations are finished and the mounting layer (view) represents exactly
1023   // what is in the most recent shadow tree
1024   // Add animation mutations to the end of our existing mutations list in this
1025   // function.
1026   ShadowViewMutationList mutationsForAnimation{};
1027   animationMutationsForFrame(surfaceId, mutationsForAnimation, now);
1028 
1029   // If any delayed removes were executed, update remaining delayed keyframes
1030 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
1031   LOG(ERROR)
1032       << "Adjust all delayed mutations based on final mutations generated by animation driver";
1033 #endif
1034   for (auto const &mutation : mutationsForAnimation) {
1035     if (mutation.type == ShadowViewMutation::Type::Remove) {
1036       adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
1037     }
1038   }
1039 
1040   mutations.insert(
1041       mutations.end(),
1042       mutationsForAnimation.begin(),
1043       mutationsForAnimation.end());
1044 
1045   // DEBUG ONLY: list existing inflight animations
1046 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
1047   LOG(ERROR) << "FINISHING DISPLAYING ONGOING inflightAnimations_!";
1048   int i = 0;
1049   int j = 0;
1050   for (auto const &inflightAnimation : inflightAnimations_) {
1051     i++;
1052     j = 0;
1053     if (inflightAnimation.completed) {
1054       continue;
1055     }
1056     for (auto &keyframe : inflightAnimation.keyFrames) {
1057       j++;
1058       if (keyframe.invalidated) {
1059         continue;
1060       }
1061       for (auto const &finalMutation : keyframe.finalMutationsForKeyFrame) {
1062         if (!finalMutation.mutatedViewIsVirtual()) {
1063           std::string msg = "Animation " + std::to_string(i) + " keyframe " +
1064               std::to_string(j) + ": Final Animation";
1065           PrintMutationInstruction(msg, finalMutation);
1066         }
1067       }
1068     }
1069   }
1070   LOG(ERROR) << "FINISHING DONE DISPLAYING ONGOING inflightAnimations_!";
1071 #endif
1072 
1073   // Signal to delegate if all animations are complete, or if we were not
1074   // animating anything and now some animation exists.
1075   if (inflightAnimationsExistInitially && inflightAnimations_.empty()) {
1076     std::lock_guard<std::mutex> lock(layoutAnimationStatusDelegateMutex_);
1077     if (layoutAnimationStatusDelegate_ != nullptr) {
1078       layoutAnimationStatusDelegate_->onAllAnimationsComplete();
1079     }
1080   } else if (
1081       !inflightAnimationsExistInitially && !inflightAnimations_.empty()) {
1082     std::lock_guard<std::mutex> lock(layoutAnimationStatusDelegateMutex_);
1083     if (layoutAnimationStatusDelegate_ != nullptr) {
1084       layoutAnimationStatusDelegate_->onAnimationStarted();
1085     }
1086   }
1087 
1088   return MountingTransaction{
1089       surfaceId, transactionNumber, std::move(mutations), telemetry};
1090 }
1091 
uiManagerDidConfigureNextLayoutAnimation(LayoutAnimation layoutAnimation) const1092 void LayoutAnimationKeyFrameManager::uiManagerDidConfigureNextLayoutAnimation(
1093     LayoutAnimation layoutAnimation) const {
1094   currentAnimation_ = std::optional<LayoutAnimation>{layoutAnimation};
1095 }
1096 
setLayoutAnimationStatusDelegate(LayoutAnimationStatusDelegate * delegate) const1097 void LayoutAnimationKeyFrameManager::setLayoutAnimationStatusDelegate(
1098     LayoutAnimationStatusDelegate *delegate) const {
1099   std::lock_guard<std::mutex> lock(layoutAnimationStatusDelegateMutex_);
1100   layoutAnimationStatusDelegate_ = delegate;
1101 }
1102 
setClockNow(std::function<uint64_t ()> now)1103 void LayoutAnimationKeyFrameManager::setClockNow(
1104     std::function<uint64_t()> now) {
1105   now_ = std::move(now);
1106 }
1107 
1108 #pragma mark - Protected
1109 
hasComponentDescriptorForShadowView(ShadowView const & shadowView) const1110 bool LayoutAnimationKeyFrameManager::hasComponentDescriptorForShadowView(
1111     ShadowView const &shadowView) const {
1112   return componentDescriptorRegistry_->hasComponentDescriptorAt(
1113       shadowView.componentHandle);
1114 }
1115 
1116 ComponentDescriptor const &
getComponentDescriptorForShadowView(ShadowView const & shadowView) const1117 LayoutAnimationKeyFrameManager::getComponentDescriptorForShadowView(
1118     ShadowView const &shadowView) const {
1119   return componentDescriptorRegistry_->at(shadowView.componentHandle);
1120 }
1121 
createInterpolatedShadowView(Float progress,ShadowView const & startingView,ShadowView const & finalView) const1122 ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView(
1123     Float progress,
1124     ShadowView const &startingView,
1125     ShadowView const &finalView) const {
1126   ABI49_0_0React_native_assert(startingView.tag > 0);
1127   ABI49_0_0React_native_assert(finalView.tag > 0);
1128   if (!hasComponentDescriptorForShadowView(startingView)) {
1129     LOG(ERROR) << "No ComponentDescriptor for ShadowView being animated: ["
1130                << startingView.tag << "]";
1131     ABI49_0_0React_native_assert(false);
1132     return finalView;
1133   }
1134 
1135   ComponentDescriptor const &componentDescriptor =
1136       getComponentDescriptorForShadowView(startingView);
1137 
1138   // Base the mutated view on the finalView, so that the following stay
1139   // consistent:
1140   // - state
1141   // - eventEmitter
1142   // For now, we do not allow interpolation of state. And we probably never
1143   // will, so make sure we always keep the mounting layer consistent with the
1144   // "final" state.
1145   auto mutatedShadowView = ShadowView(finalView);
1146   ABI49_0_0React_native_assert(mutatedShadowView.tag > 0);
1147 
1148   ABI49_0_0React_native_assert(startingView.props != nullptr);
1149   ABI49_0_0React_native_assert(finalView.props != nullptr);
1150   if (startingView.props == nullptr || finalView.props == nullptr) {
1151     return finalView;
1152   }
1153 
1154   // Animate opacity or scale/transform
1155   PropsParserContext propsParserContext{
1156       finalView.surfaceId, *contextContainer_};
1157   mutatedShadowView.props = componentDescriptor.interpolateProps(
1158       propsParserContext, progress, startingView.props, finalView.props);
1159   ABI49_0_0React_native_assert(mutatedShadowView.props != nullptr);
1160   if (mutatedShadowView.props == nullptr) {
1161     return finalView;
1162   }
1163 
1164   // Interpolate LayoutMetrics
1165   LayoutMetrics const &finalLayoutMetrics = finalView.layoutMetrics;
1166   LayoutMetrics const &baselineLayoutMetrics = startingView.layoutMetrics;
1167   LayoutMetrics interpolatedLayoutMetrics = finalLayoutMetrics;
1168   interpolatedLayoutMetrics.frame.origin.x = interpolateFloats(
1169       progress,
1170       baselineLayoutMetrics.frame.origin.x,
1171       finalLayoutMetrics.frame.origin.x);
1172   interpolatedLayoutMetrics.frame.origin.y = interpolateFloats(
1173       progress,
1174       baselineLayoutMetrics.frame.origin.y,
1175       finalLayoutMetrics.frame.origin.y);
1176   interpolatedLayoutMetrics.frame.size.width = interpolateFloats(
1177       progress,
1178       baselineLayoutMetrics.frame.size.width,
1179       finalLayoutMetrics.frame.size.width);
1180   interpolatedLayoutMetrics.frame.size.height = interpolateFloats(
1181       progress,
1182       baselineLayoutMetrics.frame.size.height,
1183       finalLayoutMetrics.frame.size.height);
1184   mutatedShadowView.layoutMetrics = interpolatedLayoutMetrics;
1185 
1186   return mutatedShadowView;
1187 }
1188 
callCallback(LayoutAnimationCallbackWrapper const & callback) const1189 void LayoutAnimationKeyFrameManager::callCallback(
1190     LayoutAnimationCallbackWrapper const &callback) const {
1191   runtimeExecutor_(
1192       [callback](jsi::Runtime &runtime) { callback.call(runtime); });
1193 }
1194 
queueFinalMutationsForCompletedKeyFrame(AnimationKeyFrame const & keyframe,ShadowViewMutation::List & mutationsList,bool interrupted,const std::string &) const1195 void LayoutAnimationKeyFrameManager::queueFinalMutationsForCompletedKeyFrame(
1196     AnimationKeyFrame const &keyframe,
1197     ShadowViewMutation::List &mutationsList,
1198     bool interrupted,
1199     const std::string & /*logPrefix*/) const {
1200   if (!keyframe.finalMutationsForKeyFrame.empty()) {
1201     // TODO: modularize this segment, it is repeated 2x in KeyFrameManager
1202     // as well.
1203     ShadowView prev = keyframe.viewPrev;
1204     for (auto const &finalMutation : keyframe.finalMutationsForKeyFrame) {
1205       PrintMutationInstruction(
1206           logPrefix + ": Queuing up Final Mutation:", finalMutation);
1207       // Copy so that if something else mutates the inflight animations,
1208       // it won't change this mutation after this point.
1209       switch (finalMutation.type) {
1210           // For CREATE/INSERT this will contain CREATE, INSERT in that order.
1211           // For REMOVE/DELETE, same.
1212         case ShadowViewMutation::Type::Create:
1213           mutationsList.push_back(ShadowViewMutation::CreateMutation(
1214               finalMutation.newChildShadowView));
1215           break;
1216         case ShadowViewMutation::Type::Delete:
1217           mutationsList.push_back(ShadowViewMutation::DeleteMutation(prev));
1218           break;
1219         case ShadowViewMutation::Type::Insert:
1220           mutationsList.push_back(ShadowViewMutation::InsertMutation(
1221               finalMutation.parentShadowView,
1222               finalMutation.newChildShadowView,
1223               finalMutation.index));
1224           break;
1225         case ShadowViewMutation::Type::Remove:
1226           mutationsList.push_back(ShadowViewMutation::RemoveMutation(
1227               finalMutation.parentShadowView, prev, finalMutation.index));
1228           break;
1229         case ShadowViewMutation::Type::RemoveDeleteTree:
1230           // Note: Currently, there is a guarantee that if RemoveDeleteTree
1231           // operations are generated, we /also/ generate corresponding
1232           // Remove/Delete operations that are marked as "redundant".
1233           // LayoutAnimations will process the redundant operations here, and
1234           // ignore this mega-op. In the future for perf reasons it would be
1235           // nice to remove the redundant operations entirely but we would need
1236           // to find a way to make the RemoveDeleteTree operation work with
1237           // LayoutAnimations (that might not be possible).
1238           break;
1239         case ShadowViewMutation::Type::Update:
1240           mutationsList.push_back(ShadowViewMutation::UpdateMutation(
1241               prev,
1242               finalMutation.newChildShadowView,
1243               finalMutation.parentShadowView));
1244           break;
1245       }
1246       if (finalMutation.newChildShadowView.tag > 0) {
1247         prev = finalMutation.newChildShadowView;
1248       }
1249     }
1250   } else {
1251     // If there's no final mutation associated, create a mutation that
1252     // corresponds to the animation being 100% complete. This is
1253     // important for, for example, INSERT mutations being animated from
1254     // opacity 0 to 1. If the animation is interrupted we must force the
1255     // View to be at opacity 1. For Android - since it passes along only
1256     // deltas, not an entire bag of props - generate an "animation"
1257     // frame corresponding to a final update for this view. Only then,
1258     // generate an update that will cause the ShadowTree to be
1259     // consistent with the Mounting layer by passing viewEnd,
1260     // unmodified, to the mounting layer. This helps with, for example,
1261     // opacity animations.
1262     // This is necessary for INSERT (create) and UPDATE (update) mutations, but
1263     // not REMOVE/DELETE mutations ("delete" animations).
1264     if (interrupted) {
1265       auto mutatedShadowView =
1266           createInterpolatedShadowView(1, keyframe.viewStart, keyframe.viewEnd);
1267       auto generatedPenultimateMutation = ShadowViewMutation::UpdateMutation(
1268           keyframe.viewPrev, mutatedShadowView, keyframe.parentView);
1269       ABI49_0_0React_native_assert(
1270           generatedPenultimateMutation.oldChildShadowView.tag > 0);
1271       ABI49_0_0React_native_assert(
1272           generatedPenultimateMutation.newChildShadowView.tag > 0);
1273       PrintMutationInstruction(
1274           "Queueing up penultimate mutation instruction - synthetic",
1275           generatedPenultimateMutation);
1276       mutationsList.push_back(generatedPenultimateMutation);
1277 
1278       auto generatedMutation = ShadowViewMutation::UpdateMutation(
1279           mutatedShadowView, keyframe.viewEnd, keyframe.parentView);
1280       ABI49_0_0React_native_assert(generatedMutation.oldChildShadowView.tag > 0);
1281       ABI49_0_0React_native_assert(generatedMutation.newChildShadowView.tag > 0);
1282       PrintMutationInstruction(
1283           "Queueing up final mutation instruction - synthetic",
1284           generatedMutation);
1285       mutationsList.push_back(generatedMutation);
1286     } else {
1287       auto mutation = ShadowViewMutation::UpdateMutation(
1288           keyframe.viewPrev, keyframe.viewEnd, keyframe.parentView);
1289       PrintMutationInstruction(
1290           logPrefix +
1291               "Animation Complete: Queuing up Final Synthetic Mutation:",
1292           mutation);
1293       ABI49_0_0React_native_assert(mutation.oldChildShadowView.tag > 0);
1294       ABI49_0_0React_native_assert(mutation.newChildShadowView.tag > 0);
1295       mutationsList.push_back(std::move(mutation));
1296     }
1297   }
1298 }
1299 
1300 #pragma mark - Private
1301 
1302 void LayoutAnimationKeyFrameManager::
adjustImmediateMutationIndicesForDelayedMutations(SurfaceId surfaceId,ShadowViewMutation & mutation,bool skipLastAnimation,bool lastAnimationOnly) const1303     adjustImmediateMutationIndicesForDelayedMutations(
1304         SurfaceId surfaceId,
1305         ShadowViewMutation &mutation,
1306         bool skipLastAnimation,
1307         bool lastAnimationOnly) const {
1308   bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove;
1309   ABI49_0_0React_native_assert(
1310       isRemoveMutation || mutation.type == ShadowViewMutation::Type::Insert);
1311 
1312   // TODO: turn all of this into a lambda and share code?
1313   if (mutation.mutatedViewIsVirtual()) {
1314     PrintMutationInstruction(
1315         "[IndexAdjustment] Not calling adjustImmediateMutationIndicesForDelayedMutations, is virtual, for:",
1316         mutation);
1317     return;
1318   }
1319 
1320   PrintMutationInstruction(
1321       "[IndexAdjustment] Calling adjustImmediateMutationIndicesForDelayedMutations for:",
1322       mutation);
1323 
1324   // First, collect all final mutations that could impact this immediate
1325   // mutation.
1326   std::vector<ShadowViewMutation const *> candidateMutations{};
1327 
1328   for (auto inflightAnimationIt =
1329            inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0);
1330        inflightAnimationIt != inflightAnimations_.rend();
1331        inflightAnimationIt++) {
1332     auto &inflightAnimation = *inflightAnimationIt;
1333     if (inflightAnimation.surfaceId != surfaceId) {
1334       continue;
1335     }
1336     if (inflightAnimation.completed) {
1337       continue;
1338     }
1339 
1340     for (auto const &animatedKeyFrame : inflightAnimation.keyFrames) {
1341       if (animatedKeyFrame.invalidated) {
1342         continue;
1343       }
1344 
1345       // Detect if they're in the same view hierarchy, but not equivalent
1346       // We've already detected direct conflicts and removed them.
1347       if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) {
1348         continue;
1349       }
1350 
1351       for (auto const &delayedMutation :
1352            animatedKeyFrame.finalMutationsForKeyFrame) {
1353         if (delayedMutation.type != ShadowViewMutation::Type::Remove) {
1354           continue;
1355         }
1356         if (delayedMutation.mutatedViewIsVirtual()) {
1357           continue;
1358         }
1359         if (delayedMutation.oldChildShadowView.tag ==
1360             (isRemoveMutation ? mutation.oldChildShadowView.tag
1361                               : mutation.newChildShadowView.tag)) {
1362           continue;
1363         }
1364 
1365         PrintMutationInstructionRelative(
1366             "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations CANDIDATE for:",
1367             mutation,
1368             delayedMutation);
1369         candidateMutations.push_back(&delayedMutation);
1370       }
1371     }
1372 
1373     if (lastAnimationOnly) {
1374       break;
1375     }
1376   }
1377 
1378   // While the mutation keeps being affected, keep checking. We use the vector
1379   // so we only perform one adjustment per delayed mutation. See comments at
1380   // bottom of adjustDelayedMutationIndicesForMutation for further explanation.
1381   bool changed = true;
1382   int adjustedDelta = 0;
1383   while (changed) {
1384     changed = false;
1385     candidateMutations.erase(
1386         std::remove_if(
1387             candidateMutations.begin(),
1388             candidateMutations.end(),
1389             [&changed, &mutation, &adjustedDelta, &isRemoveMutation](
1390                 ShadowViewMutation const *candidateMutation) {
1391               bool indexConflicts =
1392                   (candidateMutation->index < mutation.index ||
1393                    (isRemoveMutation &&
1394                     candidateMutation->index == mutation.index));
1395               if (indexConflicts) {
1396                 mutation.index++;
1397                 adjustedDelta++;
1398                 changed = true;
1399                 PrintMutationInstructionRelative(
1400                     "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting mutation UPWARD",
1401                     mutation,
1402                     *candidateMutation);
1403                 return true;
1404               }
1405               return false;
1406             }),
1407         candidateMutations.end());
1408   }
1409 }
1410 
adjustDelayedMutationIndicesForMutation(SurfaceId surfaceId,ShadowViewMutation const & mutation,bool skipLastAnimation) const1411 void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation(
1412     SurfaceId surfaceId,
1413     ShadowViewMutation const &mutation,
1414     bool skipLastAnimation) const {
1415   bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove;
1416   bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert;
1417   auto tag = isRemoveMutation ? mutation.oldChildShadowView.tag
1418                               : mutation.newChildShadowView.tag;
1419   ABI49_0_0React_native_assert(isRemoveMutation || isInsertMutation);
1420 
1421   if (mutation.mutatedViewIsVirtual()) {
1422     PrintMutationInstruction(
1423         "[IndexAdjustment] Not calling adjustDelayedMutationIndicesForMutation, is virtual, for:",
1424         mutation);
1425     return;
1426   }
1427 
1428   // First, collect all final mutations that could impact this immediate
1429   // mutation.
1430   std::vector<ShadowViewMutation *> candidateMutations{};
1431 
1432   for (auto inflightAnimationIt =
1433            inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0);
1434        inflightAnimationIt != inflightAnimations_.rend();
1435        inflightAnimationIt++) {
1436     auto &inflightAnimation = *inflightAnimationIt;
1437 
1438     if (inflightAnimation.surfaceId != surfaceId) {
1439       continue;
1440     }
1441     if (inflightAnimation.completed) {
1442       continue;
1443     }
1444 
1445     for (auto &animatedKeyFrame : inflightAnimation.keyFrames) {
1446       if (animatedKeyFrame.invalidated) {
1447         continue;
1448       }
1449 
1450       // Detect if they're in the same view hierarchy, but not equivalent
1451       // (We've already detected direct conflicts and handled them above)
1452       if (animatedKeyFrame.parentView.tag != mutation.parentShadowView.tag) {
1453         continue;
1454       }
1455 
1456       for (auto &finalAnimationMutation :
1457            animatedKeyFrame.finalMutationsForKeyFrame) {
1458         if (finalAnimationMutation.oldChildShadowView.tag == tag) {
1459           continue;
1460         }
1461 
1462         if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) {
1463           continue;
1464         }
1465         if (finalAnimationMutation.mutatedViewIsVirtual()) {
1466           continue;
1467         }
1468 
1469         PrintMutationInstructionRelative(
1470             "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:",
1471             mutation,
1472             finalAnimationMutation);
1473         candidateMutations.push_back(&finalAnimationMutation);
1474       }
1475     }
1476   }
1477 
1478   // Because the finalAnimations are not sorted in any way, it is possible to
1479   // have some sequence like:
1480   // * DELAYED REMOVE 10 from {TAG}
1481   // * DELAYED REMOVE 9 from {TAG}
1482   // * ...
1483   // * DELAYED REMOVE 5 from {TAG}
1484   // with mutation: INSERT 6/REMOVE 6. This would cause the first few mutations
1485   // to *not* be adjusted, even though they would be impacted by mutation or
1486   // vice-versa after later adjustments are applied. Therefore, we just keep
1487   // recursing while there are any changes. This isn't great, but is good enough
1488   // for now until we change these data-structures.
1489   bool changed = true;
1490   while (changed) {
1491     changed = false;
1492     candidateMutations.erase(
1493         std::remove_if(
1494             candidateMutations.begin(),
1495             candidateMutations.end(),
1496             [&mutation, &isRemoveMutation, &isInsertMutation, &changed](
1497                 ShadowViewMutation *candidateMutation) {
1498               if (isRemoveMutation &&
1499                   mutation.index <= candidateMutation->index) {
1500                 candidateMutation->index--;
1501                 changed = true;
1502                 PrintMutationInstructionRelative(
1503                     "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: Adjusting mutation DOWNWARD",
1504                     mutation,
1505                     *candidateMutation);
1506                 return true;
1507               } else if (
1508                   isInsertMutation &&
1509                   mutation.index <= candidateMutation->index) {
1510                 candidateMutation->index++;
1511                 changed = true;
1512                 PrintMutationInstructionRelative(
1513                     "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: Adjusting mutation UPWARD",
1514                     mutation,
1515                     *candidateMutation);
1516                 return true;
1517               }
1518               return false;
1519             }),
1520         candidateMutations.end());
1521   }
1522 }
1523 
getAndEraseConflictingAnimations(SurfaceId surfaceId,ShadowViewMutationList const & mutations,std::vector<AnimationKeyFrame> & conflictingAnimations) const1524 void LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations(
1525     SurfaceId surfaceId,
1526     ShadowViewMutationList const &mutations,
1527     std::vector<AnimationKeyFrame> &conflictingAnimations) const {
1528   ShadowViewMutationList localConflictingMutations{};
1529   for (auto const &mutation : mutations) {
1530     if (mutation.type == ShadowViewMutation::Type::RemoveDeleteTree) {
1531       continue;
1532     }
1533 
1534     bool mutationIsCreateOrDelete =
1535         mutation.type == ShadowViewMutation::Type::Create ||
1536         mutation.type == ShadowViewMutation::Type::Delete;
1537     auto const &baselineShadowView =
1538         (mutation.type == ShadowViewMutation::Type::Insert ||
1539          mutation.type == ShadowViewMutation::Type::Create)
1540         ? mutation.newChildShadowView
1541         : mutation.oldChildShadowView;
1542     auto baselineTag = baselineShadowView.tag;
1543 
1544     for (auto &inflightAnimation : inflightAnimations_) {
1545       if (inflightAnimation.surfaceId != surfaceId) {
1546         continue;
1547       }
1548       if (inflightAnimation.completed) {
1549         continue;
1550       }
1551 
1552       for (auto it = inflightAnimation.keyFrames.begin();
1553            it != inflightAnimation.keyFrames.end();) {
1554         auto &animatedKeyFrame = *it;
1555 
1556         if (animatedKeyFrame.invalidated) {
1557           continue;
1558         }
1559 
1560         // A conflict is when either: the animated node itself is mutated
1561         // directly; or, the parent of the node is created or deleted. In cases
1562         // of reparenting - say, the parent is deleted but the node was moved to
1563         // a different parent first - the reparenting (remove/insert) conflict
1564         // will be detected before we process the parent DELETE.
1565         // Parent deletion is important because deleting a parent recursively
1566         // deletes all children. If we previously deferred deletion of a child,
1567         // we need to force deletion/removal to happen immediately.
1568         bool conflicting = animatedKeyFrame.tag == baselineTag ||
1569             (mutationIsCreateOrDelete &&
1570              animatedKeyFrame.parentView.tag == baselineTag &&
1571              animatedKeyFrame.parentView.tag != 0);
1572 
1573         // Conflicting animation detected: if we're mutating a tag under
1574         // animation, or deleting the parent of a tag under animation, or
1575         // reparenting.
1576         if (conflicting) {
1577           animatedKeyFrame.invalidated = true;
1578 
1579           // We construct a list of all conflicting animations, whether or not
1580           // they have a "final mutation" to execute. This is important with,
1581           // for example, "insert" mutations where the final update needs to set
1582           // opacity to "1", even if there's no final ShadowNode update.
1583           // TODO: don't animate virtual views in the first place?
1584           bool isVirtual = false;
1585           for (const auto &finalMutationForKeyFrame :
1586                animatedKeyFrame.finalMutationsForKeyFrame) {
1587             isVirtual =
1588                 isVirtual || finalMutationForKeyFrame.mutatedViewIsVirtual();
1589 
1590 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
1591             PrintMutationInstructionRelative(
1592                 "Found mutation that conflicts with existing in-flight animation:",
1593                 mutation,
1594                 finalMutationForKeyFrame);
1595 #endif
1596           }
1597 
1598           conflictingAnimations.push_back(animatedKeyFrame);
1599           for (const auto &finalMutationForKeyFrame :
1600                animatedKeyFrame.finalMutationsForKeyFrame) {
1601             if (!isVirtual ||
1602                 finalMutationForKeyFrame.type ==
1603                     ShadowViewMutation::Type::Delete) {
1604               localConflictingMutations.push_back(finalMutationForKeyFrame);
1605             }
1606           }
1607 
1608           // Delete from existing animation
1609           it = inflightAnimation.keyFrames.erase(it);
1610         } else {
1611           it++;
1612         }
1613       }
1614     }
1615   }
1616 
1617   // Recurse, in case conflicting mutations conflict with other existing
1618   // animations
1619   if (!localConflictingMutations.empty()) {
1620     getAndEraseConflictingAnimations(
1621         surfaceId, localConflictingMutations, conflictingAnimations);
1622   }
1623 }
1624 
deleteAnimationsForStoppedSurfaces() const1625 void LayoutAnimationKeyFrameManager::deleteAnimationsForStoppedSurfaces()
1626     const {
1627   bool inflightAnimationsExistInitially = !inflightAnimations_.empty();
1628 
1629   // Execute stopSurface on any ongoing animations
1630   if (inflightAnimationsExistInitially) {
1631     butter::set<SurfaceId> surfaceIdsToStop{};
1632     {
1633       std::lock_guard<std::mutex> lock(surfaceIdsToStopMutex_);
1634       surfaceIdsToStop = surfaceIdsToStop_;
1635       surfaceIdsToStop_.clear();
1636     }
1637 
1638 #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
1639     std::ostringstream surfaceIdsStr;
1640     std::copy(
1641         surfaceIdsToStop.begin(),
1642         surfaceIdsToStop.end(),
1643         std::ostream_iterator<SurfaceId>(surfaceIdsStr, ", "));
1644     LOG(ERROR) << "LayoutAnimations: stopping animations due to stopSurface on "
1645                << surfaceIdsStr.str();
1646 #endif
1647 
1648     for (auto it = inflightAnimations_.begin();
1649          it != inflightAnimations_.end();) {
1650       const auto &animation = *it;
1651       if (surfaceIdsToStop.find(animation.surfaceId) !=
1652           surfaceIdsToStop.end()) {
1653         it = inflightAnimations_.erase(it);
1654       } else {
1655         it++;
1656       }
1657     }
1658   }
1659 }
1660 
1661 } // namespace ABI49_0_0facebook::ABI49_0_0React
1662