diff --git a/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp b/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp index 53bc6f9f2fd..082f7ef58d2 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.cpp @@ -52,10 +52,15 @@ bool ShadowTreeRegistry::visit( } void ShadowTreeRegistry::enumerate( - std::function const &callback) const { + std::function const + &callback) const { std::shared_lock lock(mutex_); + auto stop = false; for (auto const &pair : registry_) { - callback(*pair.second); + callback(*pair.second, stop); + if (stop) { + return; + } } } diff --git a/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.h b/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.h index 5e520f96398..5eab8e760af 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.h +++ b/ReactCommon/react/renderer/mounting/ShadowTreeRegistry.h @@ -54,10 +54,12 @@ class ShadowTreeRegistry final { /* * Enumerates all stored shadow trees. + * Set `stop` to `true` to interrupt the enumeration. * Can be called from any thread. */ void enumerate( - std::function const &callback) const; + std::function const + &callback) const; private: mutable butter::shared_mutex mutex_; diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index fc0a1641ed6..656f7c852c9 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -151,7 +151,7 @@ Scheduler::~Scheduler() { // Then, let's verify that the requirement was satisfied. auto surfaceIds = std::vector{}; uiManager_->getShadowTreeRegistry().enumerate( - [&surfaceIds](ShadowTree const &shadowTree) { + [&surfaceIds](ShadowTree const &shadowTree, bool &) { surfaceIds.push_back(shadowTree.getSurfaceId()); }); diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index 32907b56a02..37ec52dc3e4 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -351,6 +351,55 @@ void UIManager::configureNextLayoutAnimation( } } +static ShadowNode::Shared findShadowNodeByTagRecursively( + ShadowNode::Shared parentShadowNode, + Tag tag) { + if (parentShadowNode->getTag() == tag) { + return parentShadowNode; + } + + for (ShadowNode::Shared const &shadowNode : parentShadowNode->getChildren()) { + auto result = findShadowNodeByTagRecursively(shadowNode, tag); + if (result) { + return result; + } + } + + return nullptr; +} + +ShadowNode::Shared UIManager::findShadowNodeByTag_DEPRECATED(Tag tag) const { + auto shadowNode = ShadowNode::Shared{}; + + shadowTreeRegistry_.enumerate([&](ShadowTree const &shadowTree, bool &stop) { + RootShadowNode const *rootShadowNode; + // The public interface of `ShadowTree` discourages accessing a stored + // pointer to a root node because of the possible data race. + // To work around this, we ask for a commit and immediately cancel it + // returning `nullptr` instead of a new shadow tree. + // We don't want to add a way to access a stored pointer to a root node + // because this `findShadowNodeByTag` is deprecated. It is only added + // to make migration to the new architecture easier. + shadowTree.tryCommit([&](RootShadowNode const &oldRootShadowNode) { + rootShadowNode = &oldRootShadowNode; + return nullptr; + }); + + if (rootShadowNode != nullptr) { + auto const &children = rootShadowNode->getChildren(); + if (!children.empty()) { + auto const &child = children.front(); + shadowNode = findShadowNodeByTagRecursively(child, tag); + if (shadowNode) { + stop = true; + } + } + } + }); + + return shadowNode; +} + void UIManager::setComponentDescriptorRegistry( const SharedComponentDescriptorRegistry &componentDescriptorRegistry) { componentDescriptorRegistry_ = componentDescriptorRegistry; @@ -440,7 +489,7 @@ void UIManager::stopSurfaceForAnimationDelegate(SurfaceId surfaceId) const { void UIManager::animationTick() const { if (animationDelegate_ != nullptr && animationDelegate_->shouldAnimateFrame()) { - shadowTreeRegistry_.enumerate([](ShadowTree const &shadowTree) { + shadowTreeRegistry_.enumerate([](ShadowTree const &shadowTree, bool &) { shadowTree.notifyDelegatesOfUpdates(); }); } diff --git a/ReactCommon/react/renderer/uimanager/UIManager.h b/ReactCommon/react/renderer/uimanager/UIManager.h index e0ba886c972..00b64d252d2 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/ReactCommon/react/renderer/uimanager/UIManager.h @@ -166,6 +166,14 @@ class UIManager final : public ShadowTreeDelegate { const ShadowNode::Shared &shadowNode, std::string const &eventType); + /* + * Iterates over all shadow nodes which are parts of all registered surfaces + * and find the one that has given `tag`. Returns `nullptr` if the node wasn't + * found. This is a temporary workaround that should not be used in any core + * functionality. + */ + ShadowNode::Shared findShadowNodeByTag_DEPRECATED(Tag tag) const; + ShadowTreeRegistry const &getShadowTreeRegistry() const; private: diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 2e31a722444..be8278e7dc4 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -675,6 +675,27 @@ jsi::Value UIManagerBinding::get( return {serialize(ReactEventPriority::Discrete)}; } + if (methodName == "findShadowNodeByTag_DEPRECATED") { + return jsi::Function::createFromHostFunction( + runtime, + name, + 1, + [uiManager]( + jsi::Runtime &runtime, + jsi::Value const &, + jsi::Value const *arguments, + size_t) -> jsi::Value { + auto shadowNode = uiManager->findShadowNodeByTag_DEPRECATED( + tagFromValue(arguments[0])); + + if (!shadowNode) { + return jsi::Value::null(); + } + + return valueFromShadowNode(runtime, shadowNode); + }); + } + return jsi::Value::undefined(); }