Files
react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp
T
Valentin Shergin 7a290d1596 Fabric: Introducing SurfaceHandler
Summary:
This implements `SurfaceHandler`, a new primitive for controlling the Surface life-cycle that ensures ownership, preserves state, maintains internal invariants, and simplifies surface manipulation from the application side.

For now, all this is an unused code. The coming diff will introduce an experiment that will route all surfaceId-based APIs to SurfaceHandler-based ones.

Changelog: [Internal] Fabric-specific internal change.

Reviewed By: sammy-SC

Differential Revision: D24290777

fbshipit-source-id: f2ff2b58e6d46e971a548f9f02113a1c783c4940
2021-01-29 07:54:19 -08:00

435 lines
14 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "UIManager.h"
#include <react/renderer/core/ShadowNodeFragment.h>
#include <react/renderer/debug/SystraceSection.h>
#include <react/renderer/graphics/Geometry.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/UIManagerCommitHook.h>
#include <glog/logging.h>
namespace facebook {
namespace react {
UIManager::~UIManager() {
LOG(WARNING) << "UIManager::~UIManager() was called (address: " << this
<< ").";
}
SharedShadowNode UIManager::createNode(
Tag tag,
std::string const &name,
SurfaceId surfaceId,
const RawProps &rawProps,
SharedEventTarget eventTarget) const {
SystraceSection s("UIManager::createNode");
auto &componentDescriptor = componentDescriptorRegistry_->at(name);
auto fallbackDescriptor =
componentDescriptorRegistry_->getFallbackComponentDescriptor();
auto const fragment = ShadowNodeFamilyFragment{tag, surfaceId, nullptr};
auto family =
componentDescriptor.createFamily(fragment, std::move(eventTarget));
auto const props = componentDescriptor.cloneProps(nullptr, rawProps);
auto const state =
componentDescriptor.createInitialState(ShadowNodeFragment{props}, family);
auto shadowNode = componentDescriptor.createShadowNode(
ShadowNodeFragment{
/* .props = */
fallbackDescriptor != nullptr &&
fallbackDescriptor->getComponentHandle() ==
componentDescriptor.getComponentHandle()
? componentDescriptor.cloneProps(
props, RawProps(folly::dynamic::object("name", name)))
: props,
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),
/* .state = */ state,
},
family);
if (delegate_) {
delegate_->uiManagerDidCreateShadowNode(shadowNode);
}
return shadowNode;
}
SharedShadowNode UIManager::cloneNode(
const ShadowNode::Shared &shadowNode,
const SharedShadowNodeSharedList &children,
const RawProps *rawProps) const {
SystraceSection s("UIManager::cloneNode");
auto &componentDescriptor = shadowNode->getComponentDescriptor();
auto clonedShadowNode = componentDescriptor.cloneShadowNode(
*shadowNode,
{
/* .props = */
rawProps ? componentDescriptor.cloneProps(
shadowNode->getProps(), *rawProps)
: ShadowNodeFragment::propsPlaceholder(),
/* .children = */ children,
});
return clonedShadowNode;
}
void UIManager::appendChild(
const ShadowNode::Shared &parentShadowNode,
const ShadowNode::Shared &childShadowNode) const {
SystraceSection s("UIManager::appendChild");
auto &componentDescriptor = parentShadowNode->getComponentDescriptor();
componentDescriptor.appendChild(parentShadowNode, childShadowNode);
}
void UIManager::completeSurface(
SurfaceId surfaceId,
SharedShadowNodeUnsharedList const &rootChildren,
ShadowTree::CommitOptions commitOptions) const {
SystraceSection s("UIManager::completeSurface");
shadowTreeRegistry_.visit(surfaceId, [&](ShadowTree const &shadowTree) {
shadowTree.commit(
[&](RootShadowNode const &oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
oldRootShadowNode,
ShadowNodeFragment{
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */ rootChildren,
});
},
commitOptions);
});
}
void UIManager::setJSResponder(
const ShadowNode::Shared &shadowNode,
const bool blockNativeResponder) const {
if (delegate_) {
delegate_->uiManagerDidSetJSResponder(
shadowNode->getSurfaceId(), shadowNode, blockNativeResponder);
}
}
void UIManager::clearJSResponder() const {
if (delegate_) {
delegate_->uiManagerDidClearJSResponder();
}
}
ShadowTree const &UIManager::startSurface(
SurfaceId surfaceId,
std::string const &moduleName,
folly::dynamic const &props,
LayoutConstraints const &layoutConstraints,
LayoutContext const &layoutContext) const {
SystraceSection s("UIManager::startSurface");
auto shadowTree = std::make_unique<ShadowTree>(
surfaceId, layoutConstraints, layoutContext, *this);
auto shadowTreePointer = shadowTree.get();
shadowTreeRegistry_.add(std::move(shadowTree));
runtimeExecutor_([=](jsi::Runtime &runtime) {
auto uiManagerBinding = UIManagerBinding::getBinding(runtime);
if (!uiManagerBinding) {
return;
}
uiManagerBinding->startSurface(runtime, surfaceId, moduleName, props);
});
return *shadowTreePointer;
}
void UIManager::stopSurface(SurfaceId surfaceId) const {
SystraceSection s("UIManager::stopSurface");
// Stop any ongoing animations.
stopSurfaceForAnimationDelegate(surfaceId);
// Waiting for all concurrent commits to be finished and unregistering the
// `ShadowTree`.
auto shadowTree = getShadowTreeRegistry().remove(surfaceId);
// As part of stopping a Surface, we need to properly destroy all
// mounted views, so we need to commit an empty tree to trigger all
// side-effects (including destroying and removing mounted views).
if (shadowTree) {
shadowTree->commitEmptyTree();
}
// We execute JavaScript/React part of the process at the very end to minimize
// any visible side-effects of stopping the Surface. Any possible commits from
// the JavaScript side will not be able to reference a `ShadowTree` and will
// fail silently.
runtimeExecutor_([=](jsi::Runtime &runtime) {
auto uiManagerBinding = UIManagerBinding::getBinding(runtime);
if (!uiManagerBinding) {
return;
}
uiManagerBinding->stopSurface(runtime, surfaceId);
});
}
ShadowNode::Shared UIManager::getNewestCloneOfShadowNode(
ShadowNode const &shadowNode) const {
auto ancestorShadowNode = ShadowNode::Shared{};
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
ancestorShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
});
if (!ancestorShadowNode) {
return nullptr;
}
auto ancestors = shadowNode.getFamily().getAncestors(*ancestorShadowNode);
if (ancestors.empty()) {
return nullptr;
}
auto pair = ancestors.rbegin();
return pair->first.get().getChildren().at(pair->second);
}
ShadowNode::Shared UIManager::findNodeAtPoint(
ShadowNode::Shared const &node,
Point point) const {
return LayoutableShadowNode::findNodeAtPoint(
getNewestCloneOfShadowNode(*node), point);
}
LayoutMetrics UIManager::getRelativeLayoutMetrics(
ShadowNode const &shadowNode,
ShadowNode const *ancestorShadowNode,
LayoutableShadowNode::LayoutInspectingPolicy policy) const {
SystraceSection s("UIManager::getRelativeLayoutMetrics");
// We might store here an owning pointer to `ancestorShadowNode` to ensure
// that the node is not deallocated during method execution lifetime.
auto owningAncestorShadowNode = ShadowNode::Shared{};
if (!ancestorShadowNode) {
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
owningAncestorShadowNode =
shadowTree.getCurrentRevision().rootShadowNode;
ancestorShadowNode = owningAncestorShadowNode.get();
});
} else {
// It is possible for JavaScript (or other callers) to have a reference
// to a previous version of ShadowNodes, but we enforce that
// metrics are only calculated on most recently committed versions.
owningAncestorShadowNode = getNewestCloneOfShadowNode(*ancestorShadowNode);
ancestorShadowNode = owningAncestorShadowNode.get();
}
auto layoutableAncestorShadowNode =
traitCast<LayoutableShadowNode const *>(ancestorShadowNode);
if (!layoutableAncestorShadowNode) {
return EmptyLayoutMetrics;
}
return LayoutableShadowNode::computeRelativeLayoutMetrics(
shadowNode.getFamily(), *layoutableAncestorShadowNode, policy);
}
void UIManager::updateState(StateUpdate const &stateUpdate) const {
auto &callback = stateUpdate.callback;
auto &family = stateUpdate.family;
auto &componentDescriptor = family->getComponentDescriptor();
shadowTreeRegistry_.visit(
family->getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.commit([&](RootShadowNode const &oldRootShadowNode) {
auto isValid = true;
auto rootNode = oldRootShadowNode.cloneTree(
*family, [&](ShadowNode const &oldShadowNode) {
auto newData =
callback(oldShadowNode.getState()->getDataPointer());
if (!newData) {
isValid = false;
// Just return something, we will discard it anyway.
return oldShadowNode.clone({});
}
auto newState =
componentDescriptor.createState(*family, newData);
return oldShadowNode.clone({
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */
ShadowNodeFragment::childrenPlaceholder(),
/* .state = */ newState,
});
});
return isValid ? std::static_pointer_cast<RootShadowNode>(rootNode)
: nullptr;
});
});
}
void UIManager::dispatchCommand(
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const args) const {
if (delegate_) {
delegate_->uiManagerDidDispatchCommand(shadowNode, commandName, args);
}
}
void UIManager::sendAccessibilityEvent(
const ShadowNode::Shared &shadowNode,
std::string const &eventType) {
if (delegate_) {
delegate_->uiManagerDidSendAccessibilityEvent(shadowNode, eventType);
}
}
void UIManager::configureNextLayoutAnimation(
jsi::Runtime &runtime,
RawValue const &config,
jsi::Value const &successCallback,
jsi::Value const &failureCallback) const {
if (animationDelegate_) {
animationDelegate_->uiManagerDidConfigureNextLayoutAnimation(
runtime,
config,
std::move(successCallback),
std::move(failureCallback));
}
}
void UIManager::setComponentDescriptorRegistry(
const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {
componentDescriptorRegistry_ = componentDescriptorRegistry;
}
void UIManager::setDelegate(UIManagerDelegate *delegate) {
delegate_ = delegate;
}
UIManagerDelegate *UIManager::getDelegate() {
return delegate_;
}
void UIManager::setBackgroundExecutor(
BackgroundExecutor const &backgroundExecutor) {
backgroundExecutor_ = backgroundExecutor;
}
void UIManager::setRuntimeExecutor(RuntimeExecutor const &runtimeExecutor) {
runtimeExecutor_ = runtimeExecutor;
}
void UIManager::visitBinding(
std::function<void(UIManagerBinding const &uiManagerBinding)> callback,
jsi::Runtime &runtime) const {
if (extractUIManagerBindingOnDemand_) {
auto uiManagerBinding = UIManagerBinding::getBinding(runtime);
if (uiManagerBinding) {
callback(*uiManagerBinding_);
}
return;
}
if (!uiManagerBinding_) {
return;
}
callback(*uiManagerBinding_);
}
ShadowTreeRegistry const &UIManager::getShadowTreeRegistry() const {
return shadowTreeRegistry_;
}
void UIManager::registerCommitHook(
UIManagerCommitHook const &commitHook) const {
std::unique_lock<better::shared_mutex> lock(commitHookMutex_);
assert(
std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook) ==
commitHooks_.end());
commitHook.commitHookWasRegistered(*this);
commitHooks_.push_back(&commitHook);
}
void UIManager::unregisterCommitHook(
UIManagerCommitHook const &commitHook) const {
std::unique_lock<better::shared_mutex> lock(commitHookMutex_);
auto iterator =
std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook);
assert(iterator != commitHooks_.end());
commitHooks_.erase(iterator);
commitHook.commitHookWasUnregistered(*this);
}
#pragma mark - ShadowTreeDelegate
RootShadowNode::Unshared UIManager::shadowTreeWillCommit(
ShadowTree const &shadowTree,
RootShadowNode::Shared const &oldRootShadowNode,
RootShadowNode::Unshared const &newRootShadowNode) const {
std::shared_lock<better::shared_mutex> lock(commitHookMutex_);
auto resultRootShadowNode = newRootShadowNode;
for (auto const *commitHook : commitHooks_) {
resultRootShadowNode = commitHook->shadowTreeWillCommit(
shadowTree, oldRootShadowNode, resultRootShadowNode);
}
return resultRootShadowNode;
}
void UIManager::shadowTreeDidFinishTransaction(
ShadowTree const &shadowTree,
MountingCoordinator::Shared const &mountingCoordinator) const {
SystraceSection s("UIManager::shadowTreeDidFinishTransaction");
if (delegate_) {
delegate_->uiManagerDidFinishTransaction(mountingCoordinator);
}
}
#pragma mark - UIManagerAnimationDelegate
void UIManager::setAnimationDelegate(UIManagerAnimationDelegate *delegate) {
animationDelegate_ = delegate;
}
void UIManager::stopSurfaceForAnimationDelegate(SurfaceId surfaceId) const {
if (animationDelegate_ != nullptr) {
animationDelegate_->stopSurface(surfaceId);
}
}
void UIManager::animationTick() {
if (animationDelegate_ != nullptr &&
animationDelegate_->shouldAnimateFrame()) {
shadowTreeRegistry_.enumerate(
[&](ShadowTree const &shadowTree, bool &stop) {
shadowTree.notifyDelegatesOfUpdates();
});
}
}
} // namespace react
} // namespace facebook