123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- * 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 "ShadowTree.h"
- #include <react/components/root/RootComponentDescriptor.h>
- #include <react/components/view/ViewShadowNode.h>
- #include <react/core/LayoutContext.h>
- #include <react/core/LayoutPrimitives.h>
- #include <react/debug/SystraceSection.h>
- #include <react/mounting/MountingTelemetry.h>
- #include <react/mounting/ShadowTreeRevision.h>
- #include <react/mounting/ShadowViewMutation.h>
- #include "ShadowTreeDelegate.h"
- #include "TreeStateReconciliation.h"
- namespace facebook {
- namespace react {
- static void updateMountedFlag(
- const SharedShadowNodeList &oldChildren,
- const SharedShadowNodeList &newChildren) {
- // This is a simplified version of Diffing algorithm that only updates
- // `mounted` flag on `ShadowNode`s. The algorithm sets "mounted" flag before
- // "unmounted" to allow `ShadowNode` detect a situation where the node was
- // remounted.
- if (&oldChildren == &newChildren) {
- // Lists are identical, nothing to do.
- return;
- }
- if (oldChildren.size() == 0 && newChildren.size() == 0) {
- // Both lists are empty, nothing to do.
- return;
- }
- int index;
- // Stage 1: Mount and unmount "updated" children.
- for (index = 0; index < oldChildren.size() && index < newChildren.size();
- index++) {
- const auto &oldChild = oldChildren[index];
- const auto &newChild = newChildren[index];
- if (oldChild == newChild) {
- // Nodes are identical, skipping the subtree.
- continue;
- }
- if (!ShadowNode::sameFamily(*oldChild, *newChild)) {
- // Totally different nodes, updating is impossible.
- break;
- }
- newChild->setMounted(true);
- oldChild->setMounted(false);
- updateMountedFlag(oldChild->getChildren(), newChild->getChildren());
- }
- int lastIndexAfterFirstStage = index;
- // State 2: Mount new children.
- for (index = lastIndexAfterFirstStage; index < newChildren.size(); index++) {
- const auto &newChild = newChildren[index];
- newChild->setMounted(true);
- updateMountedFlag({}, newChild->getChildren());
- }
- // State 3: Unmount old children.
- for (index = lastIndexAfterFirstStage; index < oldChildren.size(); index++) {
- const auto &oldChild = oldChildren[index];
- oldChild->setMounted(false);
- updateMountedFlag(oldChild->getChildren(), {});
- }
- }
- ShadowTree::ShadowTree(
- SurfaceId surfaceId,
- LayoutConstraints const &layoutConstraints,
- LayoutContext const &layoutContext,
- RootComponentDescriptor const &rootComponentDescriptor,
- ShadowTreeDelegate const &delegate)
- : surfaceId_(surfaceId), delegate_(delegate) {
- const auto noopEventEmitter = std::make_shared<const ViewEventEmitter>(
- nullptr, -1, std::shared_ptr<const EventDispatcher>());
- const auto props = std::make_shared<const RootProps>(
- *RootShadowNode::defaultSharedProps(), layoutConstraints, layoutContext);
- auto family = rootComponentDescriptor.createFamily(
- ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter},
- nullptr);
- rootShadowNode_ = std::static_pointer_cast<const RootShadowNode>(
- rootComponentDescriptor.createShadowNode(
- ShadowNodeFragment{
- /* .props = */ props,
- },
- family));
- mountingCoordinator_ = std::make_shared<MountingCoordinator const>(
- ShadowTreeRevision{rootShadowNode_, 0, {}});
- }
- ShadowTree::~ShadowTree() {
- mountingCoordinator_->revoke();
- }
- Tag ShadowTree::getSurfaceId() const {
- return surfaceId_;
- }
- MountingCoordinator::Shared ShadowTree::getMountingCoordinator() const {
- return mountingCoordinator_;
- }
- void ShadowTree::commit(
- ShadowTreeCommitTransaction transaction,
- bool enableStateReconciliation) const {
- SystraceSection s("ShadowTree::commit");
- int attempts = 0;
- while (true) {
- attempts++;
- if (tryCommit(transaction, enableStateReconciliation)) {
- return;
- }
- // After multiple attempts, we failed to commit the transaction.
- // Something internally went terribly wrong.
- assert(attempts < 1024);
- }
- }
- bool ShadowTree::tryCommit(
- ShadowTreeCommitTransaction transaction,
- bool enableStateReconciliation) const {
- SystraceSection s("ShadowTree::tryCommit");
- auto telemetry = MountingTelemetry{};
- telemetry.willCommit();
- RootShadowNode::Shared oldRootShadowNode;
- {
- // Reading `rootShadowNode_` in shared manner.
- std::shared_lock<better::shared_mutex> lock(commitMutex_);
- oldRootShadowNode = rootShadowNode_;
- }
- RootShadowNode::Unshared newRootShadowNode = transaction(oldRootShadowNode);
- if (!newRootShadowNode) {
- return false;
- }
- // Compare state revisions of old and new root
- // Children of the root node may be mutated in-place
- if (enableStateReconciliation) {
- UnsharedShadowNode reconciledNode =
- reconcileStateWithTree(newRootShadowNode.get(), oldRootShadowNode);
- if (reconciledNode != nullptr) {
- newRootShadowNode = std::make_shared<RootShadowNode>(
- *reconciledNode, ShadowNodeFragment{});
- }
- }
- // Layout nodes
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
- affectedLayoutableNodes.reserve(1024);
- telemetry.willLayout();
- newRootShadowNode->layoutIfNeeded(&affectedLayoutableNodes);
- telemetry.didLayout();
- // Seal the shadow node so it can no longer be mutated
- newRootShadowNode->sealRecursive();
- auto revisionNumber = ShadowTreeRevision::Number{};
- {
- // Updating `rootShadowNode_` in unique manner if it hasn't changed.
- std::unique_lock<better::shared_mutex> lock(commitMutex_);
- if (rootShadowNode_ != oldRootShadowNode) {
- return false;
- }
- rootShadowNode_ = newRootShadowNode;
- {
- std::lock_guard<std::mutex> dispatchLock(EventEmitter::DispatchMutex());
- updateMountedFlag(
- oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
- }
- revisionNumber_++;
- revisionNumber = revisionNumber_;
- }
- emitLayoutEvents(affectedLayoutableNodes);
- telemetry.didCommit();
- mountingCoordinator_->push(
- ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry});
- delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_);
- return true;
- }
- void ShadowTree::commitEmptyTree() const {
- commit(
- [](RootShadowNode::Shared const &oldRootShadowNode)
- -> RootShadowNode::Unshared {
- return std::make_shared<RootShadowNode>(
- *oldRootShadowNode,
- ShadowNodeFragment{
- /* .props = */ ShadowNodeFragment::propsPlaceholder(),
- /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
- });
- });
- }
- void ShadowTree::emitLayoutEvents(
- std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
- SystraceSection s("ShadowTree::emitLayoutEvents");
- for (auto const *layoutableNode : affectedLayoutableNodes) {
- // Only instances of `ViewShadowNode` (and subclasses) are supported.
- auto const &viewShadowNode =
- static_cast<ViewShadowNode const &>(*layoutableNode);
- auto const &viewEventEmitter = static_cast<ViewEventEmitter const &>(
- *viewShadowNode.getEventEmitter());
- // Checking if the `onLayout` event was requested for the particular Shadow
- // Node.
- auto const &viewProps =
- static_cast<ViewProps const &>(*viewShadowNode.getProps());
- if (!viewProps.onLayout) {
- continue;
- }
- viewEventEmitter.onLayout(layoutableNode->getLayoutMetrics());
- }
- }
- } // namespace react
- } // namespace facebook
|