diff --git a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h index 096218ab9659955cd6272c97181bce3c893ed591..1a8e25fc8295b3ac943130709bf063ea41a50585 100644 --- a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h +++ b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h @@ -57,11 +57,11 @@ class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDele const SharedComponentDescriptorRegistry &componentDescriptorRegistry, const std::shared_ptr &contextContainer, jsi::Runtime &uiRuntime, - const std::shared_ptr &uiScheduler + const std::shared_ptr &uiScheduler, + const std::shared_ptr &uiManager #ifdef ANDROID , const PreserveMountedTagsFunction &filterUnmountedTagsFunction, - const std::shared_ptr &uiManager, const std::shared_ptr &jsInvoker #endif ) @@ -69,11 +69,11 @@ class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDele contextContainer_(contextContainer), componentDescriptorRegistry_(componentDescriptorRegistry), uiRuntime_(uiRuntime), - uiScheduler_(uiScheduler) + uiScheduler_(uiScheduler), + uiManager_(uiManager) #ifdef ANDROID , preserveMountedTags_(filterUnmountedTagsFunction), - uiManager_(uiManager), jsInvoker_(jsInvoker) #endif { @@ -93,10 +93,10 @@ class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDele SharedComponentDescriptorRegistry componentDescriptorRegistry_; jsi::Runtime &uiRuntime_; const std::shared_ptr uiScheduler_; + std::shared_ptr uiManager_; PreserveMountedTagsFunction preserveMountedTags_; #ifdef ANDROID - std::shared_ptr uiManager_; std::shared_ptr jsInvoker_; void restoreOpacityInCaseOfFlakyEnteringAnimation(SurfaceId surfaceId) const; diff --git a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h index eca44e4cf651d16e9741806004ce9119c85d07d6..e39c79a5d7b52659106ed6fe6fbcbbc048bf4787 100644 --- a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h +++ b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h @@ -66,11 +66,11 @@ struct LayoutAnimationsProxy_Experimental : public LayoutAnimationsProxyCommon, const SharedComponentDescriptorRegistry &componentDescriptorRegistry, const std::shared_ptr &contextContainer, jsi::Runtime &uiRuntime, - const std::shared_ptr &uiScheduler + const std::shared_ptr &uiScheduler, + const std::shared_ptr &uiManager #ifdef ANDROID , const PreserveMountedTagsFunction &filterUnmountedTagsFunction, - const std::shared_ptr &uiManager, const std::shared_ptr &jsInvoker #endif ) @@ -79,11 +79,11 @@ struct LayoutAnimationsProxy_Experimental : public LayoutAnimationsProxyCommon, componentDescriptorRegistry, contextContainer, uiRuntime, - uiScheduler + uiScheduler, + uiManager #ifdef ANDROID , filterUnmountedTagsFunction, - uiManager, jsInvoker #endif ), diff --git a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp index 83ef7430b923b6b3b99368ee8072168769110cd0..2affd12822ab19bdc90963d3ce8ca1e6bb0d43b7 100644 --- a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp +++ b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -53,14 +54,37 @@ std::optional LayoutAnimationsProxy_Legacy::pullTransaction parseRemoveMutations(movedViews, mutations, roots); - auto shouldAnimate = !surfacesToRemove_.contains(surfaceId); - surfacesToRemove_.erase(surfaceId); + // Consume the teardown mark only on the transaction that actually clears + // the root — pulls emitted for animation frames must not eat it early. + auto shouldAnimate = true; + const auto removesRootChildren = std::ranges::any_of(mutations, [surfaceId](const auto &mutation) { + return mutation.type == ShadowViewMutation::Remove && mutation.parentTag == surfaceId; + }); + if (removesRootChildren) { + shouldAnimate = surfacesToRemove_.erase(surfaceId) == 0; + } handleRemovals(filteredMutations, roots, deadNodes, shouldAnimate); handleUpdatesAndEnterings(filteredMutations, movedViews, mutations, propsParserContext, surfaceId); addOngoingAnimations(surfaceId, filteredMutations); + // The LayoutAnimationDriver can emit a final keyframe update in the same + // transaction as the deferred Remove/Delete it withheld for a delete + // animation. We emit removals before updates, so such an update would + // otherwise reach the mounting layer after its view was deleted. + std::unordered_set deletedTags; + for (const auto &mutation : filteredMutations) { + if (mutation.type == ShadowViewMutation::Delete) { + deletedTags.insert(mutation.oldChildShadowView.tag); + } + } + if (!deletedTags.empty()) { + std::erase_if(filteredMutations, [&deletedTags](const auto &mutation) { + return mutation.type == ShadowViewMutation::Update && deletedTags.contains(mutation.newChildShadowView.tag); + }); + } + return MountingTransaction{surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } @@ -947,23 +971,22 @@ inline bool MutationNode::isMutationNode() { return true; } -// UIManagerAnimationDelegate - -void LayoutAnimationsProxy_Legacy::uiManagerDidConfigureNextLayoutAnimation( - jsi::Runtime &runtime, - const RawValue &config, - const jsi::Value &successCallbackValue, - const jsi::Value &failureCallbackValue) const {} +// UIManagerCommitHook -void LayoutAnimationsProxy_Legacy::setComponentDescriptorRegistry( - const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {} - -bool LayoutAnimationsProxy_Legacy::shouldAnimateFrame() const { - return false; -} - -void LayoutAnimationsProxy_Legacy::stopSurface(SurfaceId surfaceId) { - surfacesToRemove_.insert(surfaceId); +// Surface teardown commits an empty root (SurfaceHandler::stop) before the +// teardown transaction is pulled — mark it so pullTransaction skips exit +// animations. Reading the ShadowTreeRegistry here instead would deadlock (#8579). +RootShadowNode::Unshared LayoutAnimationsProxy_Legacy::shadowTreeWillCommit( + const ShadowTree &shadowTree, + const RootShadowNode::Shared & /*oldRootShadowNode*/, + const RootShadowNode::Unshared &newRootShadowNode) noexcept { + auto lock = std::unique_lock(mutex); + if (newRootShadowNode->getChildren().empty()) { + surfacesToRemove_.insert(shadowTree.getSurfaceId()); + } else { + surfacesToRemove_.erase(shadowTree.getSurfaceId()); + } + return newRootShadowNode; } } // namespace reanimated diff --git a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h index e9a5e9959e89ec33cee179ddb907c17f6dfbd3de..a2c89041518cd71e8ba5ac62ef89c0022d197c9b 100644 --- a/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h +++ b/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h @@ -3,8 +3,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -102,7 +102,7 @@ struct SurfaceContext { }; struct LayoutAnimationsProxy_Legacy : public LayoutAnimationsProxyCommon, - public UIManagerAnimationDelegate, + public UIManagerCommitHook, public std::enable_shared_from_this { mutable std::unordered_map> nodeForTag_; mutable std::recursive_mutex mutex; @@ -116,11 +116,11 @@ struct LayoutAnimationsProxy_Legacy : public LayoutAnimationsProxyCommon, const SharedComponentDescriptorRegistry &componentDescriptorRegistry, const std::shared_ptr &contextContainer, jsi::Runtime &uiRuntime, - const std::shared_ptr &uiScheduler + const std::shared_ptr &uiScheduler, + const std::shared_ptr &uiManager #ifdef ANDROID , const PreserveMountedTagsFunction &filterUnmountedTagsFunction, - const std::shared_ptr &uiManager, const std::shared_ptr &jsInvoker #endif ) @@ -129,14 +129,19 @@ struct LayoutAnimationsProxy_Legacy : public LayoutAnimationsProxyCommon, componentDescriptorRegistry, contextContainer, uiRuntime, - uiScheduler + uiScheduler, + uiManager #ifdef ANDROID , filterUnmountedTagsFunction, - uiManager, jsInvoker #endif ) { + uiManager->registerCommitHook(*this); + } + + ~LayoutAnimationsProxy_Legacy() override { + uiManager_->unregisterCommitHook(*this); } void startEnteringAnimation(const int tag, ShadowViewMutation &mutation) const; @@ -202,19 +207,15 @@ struct LayoutAnimationsProxy_Legacy : public LayoutAnimationsProxyCommon, const TransactionTelemetry &telemetry, ShadowViewMutationList mutations) const override; - // UIManagerAnimationDelegate - - void uiManagerDidConfigureNextLayoutAnimation( - jsi::Runtime &runtime, - const RawValue &config, - const jsi::Value &successCallbackValue, - const jsi::Value &failureCallbackValue) const override; - - void setComponentDescriptorRegistry(const SharedComponentDescriptorRegistry &componentDescriptorRegistry) override; + // UIManagerCommitHook - bool shouldAnimateFrame() const override; + void commitHookWasRegistered(const UIManager &uiManager) noexcept override {} + void commitHookWasUnregistered(const UIManager &uiManager) noexcept override {} - void stopSurface(SurfaceId surfaceId) override; + RootShadowNode::Unshared shadowTreeWillCommit( + const ShadowTree &shadowTree, + const RootShadowNode::Shared &oldRootShadowNode, + const RootShadowNode::Unshared &newRootShadowNode) noexcept override; }; } // namespace reanimated diff --git a/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index 9ade22bf773005613048a00c47b35767628e86c6..688fdfb82715261fd08a63126c8691b0095fbf56 100644 --- a/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -1306,11 +1306,11 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { componentDescriptorRegistry, scheduler->getContextContainer(), getJSIRuntimeFromWorkletRuntime(uiRuntime_), - uiScheduler_ + uiScheduler_, + uiManager_ #ifdef ANDROID , filterUnmountedTagsFunction_, - uiManager_, jsInvoker_ #endif ); @@ -1319,22 +1319,19 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { #endif layoutAnimationsProxy_ = std::move(layoutAnimationsProxyExperimental); } else { - auto layoutAnimationsProxyLegacy = std::make_shared( + layoutAnimationsProxy_ = std::make_shared( layoutAnimationsManager_, componentDescriptorRegistry, scheduler->getContextContainer(), getJSIRuntimeFromWorkletRuntime(uiRuntime_), - uiScheduler_ + uiScheduler_, + uiManager_ #ifdef ANDROID , filterUnmountedTagsFunction_, - uiManager_, jsInvoker_ #endif ); - // TODO (future): support in experimental - uiManager_->setAnimationDelegate(layoutAnimationsProxyLegacy.get()); - layoutAnimationsProxy_ = std::move(layoutAnimationsProxyLegacy); } } }