first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB react_renderer_observers_mutation_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_renderer_observers_mutation OBJECT ${react_renderer_observers_mutation_SRC})
target_include_directories(react_renderer_observers_mutation PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_renderer_observers_mutation
react_cxxreact
react_renderer_core
react_renderer_uimanager
react_renderer_mounting
react_bridging
)
target_compile_reactnative_options(react_renderer_observers_mutation PRIVATE)
target_compile_options(react_renderer_observers_mutation PRIVATE -Wpedantic)

View File

@@ -0,0 +1,187 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MutationObserver.h"
#include <react/renderer/core/ShadowNodeTraits.h>
#include <react/renderer/uimanager/primitives.h>
namespace facebook::react {
MutationObserver::MutationObserver(MutationObserverId mutationObserverId)
: mutationObserverId_(mutationObserverId) {}
void MutationObserver::observe(
std::shared_ptr<const ShadowNodeFamily> targetShadowNodeFamily,
bool observeSubtree) {
auto& list = observeSubtree ? deeplyObservedShadowNodeFamilies_
: shallowlyObservedShadowNodeFamilies_;
auto& otherList = observeSubtree ? shallowlyObservedShadowNodeFamilies_
: deeplyObservedShadowNodeFamilies_;
if (std::find(list.begin(), list.end(), targetShadowNodeFamily) !=
list.end()) {
// It is already being observed correctly.
return;
}
auto it =
std::find(otherList.begin(), otherList.end(), targetShadowNodeFamily);
if (it != otherList.end()) {
// It is being observed incorrectly.
otherList.erase(it);
}
list.push_back(targetShadowNodeFamily);
}
static std::shared_ptr<const ShadowNode> getShadowNodeInTree(
const ShadowNodeFamily& shadowNodeFamily,
const ShadowNode& newTree) {
auto ancestors = shadowNodeFamily.getAncestors(newTree);
if (ancestors.empty()) {
return nullptr;
}
auto pair = ancestors.rbegin();
return pair->first.get().getChildren().at(pair->second);
}
static std::shared_ptr<const ShadowNode> findNodeOfSameFamily(
const std::vector<std::shared_ptr<const ShadowNode>>& list,
const ShadowNode& node) {
for (auto& current : list) {
if (ShadowNode::sameFamily(node, *current)) {
return current;
}
}
return nullptr;
}
void MutationObserver::recordMutations(
const RootShadowNode& oldRootShadowNode,
const RootShadowNode& newRootShadowNode,
std::vector<MutationRecord>& recordedMutations) const {
// This tracks the nodes that have already been processed by this observer,
// so we avoid unnecessary work and duplicated entries.
SetOfShadowNodePointers processedNodes;
// We go over the deeply observed nodes first to avoid skipping nodes that
// have only been checked shallowly.
for (const auto& targetShadowNodeFamily : deeplyObservedShadowNodeFamilies_) {
recordMutationsInTarget(
*targetShadowNodeFamily,
oldRootShadowNode,
newRootShadowNode,
true,
recordedMutations,
processedNodes);
}
for (const auto& targetShadowNodeFamily :
shallowlyObservedShadowNodeFamilies_) {
recordMutationsInTarget(
*targetShadowNodeFamily,
oldRootShadowNode,
newRootShadowNode,
false,
recordedMutations,
processedNodes);
}
}
void MutationObserver::recordMutationsInTarget(
const ShadowNodeFamily& targetShadowNodeFamily,
const RootShadowNode& oldRootShadowNode,
const RootShadowNode& newRootShadowNode,
bool observeSubtree,
std::vector<MutationRecord>& recordedMutations,
SetOfShadowNodePointers& processedNodes) const {
// If the node isnt't present in the old tree, it's either:
// - A new node. In that case, the mutation happened in its parent, not in the
// node itself.
// - A non-existent node. In that case, there are no new mutations.
auto oldTargetShadowNode =
getShadowNodeInTree(targetShadowNodeFamily, oldRootShadowNode);
if (!oldTargetShadowNode) {
return;
}
// If the node isn't present in the new tree (and we didn't return in the
// previous check), it means the whole node was removed. In that case we don't
// record any mutations in the node itself (maybe in its parent if there are
// other observers set up).
auto newTargetShadowNode =
getShadowNodeInTree(targetShadowNodeFamily, newRootShadowNode);
if (!newTargetShadowNode) {
return;
}
recordMutationsInSubtrees(
oldTargetShadowNode,
newTargetShadowNode,
observeSubtree,
recordedMutations,
processedNodes);
}
void MutationObserver::recordMutationsInSubtrees(
const std::shared_ptr<const ShadowNode>& oldNode,
const std::shared_ptr<const ShadowNode>& newNode,
bool observeSubtree,
std::vector<MutationRecord>& recordedMutations,
SetOfShadowNodePointers& processedNodes) const {
bool isSameNode = oldNode.get() == newNode.get();
// If the nodes are referentially equal, their children are also the same.
if (isSameNode ||
processedNodes.find(oldNode.get()) != processedNodes.end()) {
return;
}
processedNodes.insert(oldNode.get());
auto oldChildren = oldNode->getChildren();
auto newChildren = newNode->getChildren();
std::vector<std::shared_ptr<const ShadowNode>> addedNodes;
std::vector<std::shared_ptr<const ShadowNode>> removedNodes;
// Check for removed nodes (and equal nodes for further inspection)
for (auto& oldChild : oldChildren) {
auto newChild = findNodeOfSameFamily(newChildren, *oldChild);
if (!newChild) {
removedNodes.push_back(oldChild);
} else if (observeSubtree) {
// Nodes are present in both tress. If `subtree` is set to true,
// we continue checking their children.
recordMutationsInSubtrees(
oldChild,
newChild,
observeSubtree,
recordedMutations,
processedNodes);
}
}
// Check for added nodes
for (auto& newChild : newChildren) {
auto oldChild = findNodeOfSameFamily(oldChildren, *newChild);
if (!oldChild) {
addedNodes.push_back(newChild);
}
}
if (!addedNodes.empty() || !removedNodes.empty()) {
recordedMutations.emplace_back(
MutationRecord{
.mutationObserverId = mutationObserverId_,
.targetShadowNode = oldNode,
.addedShadowNodes = std::move(addedNodes),
.removedShadowNodes = std::move(removedNodes)});
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/core/ShadowNodeFamily.h>
namespace facebook::react {
using MutationObserverId = int32_t;
struct MutationRecord {
MutationObserverId mutationObserverId;
std::shared_ptr<const ShadowNode> targetShadowNode;
std::vector<std::shared_ptr<const ShadowNode>> addedShadowNodes;
std::vector<std::shared_ptr<const ShadowNode>> removedShadowNodes;
};
class MutationObserver {
public:
explicit MutationObserver(MutationObserverId mutationObserverId);
// delete copy constructor
MutationObserver(const MutationObserver &) = delete;
// delete copy assignment
MutationObserver &operator=(const MutationObserver &) = delete;
// allow move constructor
MutationObserver(MutationObserver &&) = default;
// allow move assignment
MutationObserver &operator=(MutationObserver &&) = default;
void observe(std::shared_ptr<const ShadowNodeFamily> targetShadowNodeFamily, bool observeSubtree);
void recordMutations(
const RootShadowNode &oldRootShadowNode,
const RootShadowNode &newRootShadowNode,
std::vector<MutationRecord> &recordedMutations) const;
private:
MutationObserverId mutationObserverId_;
std::vector<std::shared_ptr<const ShadowNodeFamily>> deeplyObservedShadowNodeFamilies_;
std::vector<std::shared_ptr<const ShadowNodeFamily>> shallowlyObservedShadowNodeFamilies_;
using SetOfShadowNodePointers = std::unordered_set<const ShadowNode *>;
void recordMutationsInTarget(
const ShadowNodeFamily &targetShadowNodeFamily,
const RootShadowNode &oldRootShadowNode,
const RootShadowNode &newRootShadowNode,
bool observeSubtree,
std::vector<MutationRecord> &recordedMutations,
SetOfShadowNodePointers &processedNodes) const;
void recordMutationsInSubtrees(
const std::shared_ptr<const ShadowNode> &oldNode,
const std::shared_ptr<const ShadowNode> &newNode,
bool observeSubtree,
std::vector<MutationRecord> &recordedMutations,
SetOfShadowNodePointers &processedNodes) const;
};
} // namespace facebook::react

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MutationObserverManager.h"
#include <cxxreact/TraceSection.h>
#include <utility>
#include "MutationObserver.h"
namespace facebook::react {
MutationObserverManager::MutationObserverManager() = default;
void MutationObserverManager::observe(
MutationObserverId mutationObserverId,
std::shared_ptr<const ShadowNode> shadowNode,
bool observeSubtree,
const UIManager& /*uiManager*/) {
TraceSection s("MutationObserverManager::observe");
auto surfaceId = shadowNode->getSurfaceId();
auto shadowNodeFamily = shadowNode->getFamilyShared();
auto& observers = observersBySurfaceId_[surfaceId];
auto observerIt = observers.find(mutationObserverId);
if (observerIt == observers.end()) {
auto observer = MutationObserver{mutationObserverId};
observer.observe(shadowNodeFamily, observeSubtree);
observers.emplace(mutationObserverId, std::move(observer));
} else {
auto& observer = observerIt->second;
observer.observe(shadowNodeFamily, observeSubtree);
}
}
void MutationObserverManager::unobserveAll(
MutationObserverId mutationObserverId) {
TraceSection s("MutationObserverManager::unobserveAll");
for (auto it = observersBySurfaceId_.begin();
it != observersBySurfaceId_.end();) {
auto& observers = it->second;
auto deleted = observers.erase(mutationObserverId);
if (deleted > 0 && observers.empty()) {
it = observersBySurfaceId_.erase(it);
} else {
++it;
}
}
}
void MutationObserverManager::connect(
UIManager& uiManager,
OnMutations&& onMutations) {
TraceSection s("MutationObserverManager::connect");
// Fail-safe in case the caller doesn't guarantee consistency.
if (commitHookRegistered_) {
return;
}
onMutations_ = std::move(onMutations);
uiManager.registerCommitHook(*this);
commitHookRegistered_ = true;
}
void MutationObserverManager::disconnect(UIManager& uiManager) {
TraceSection s("MutationObserverManager::disconnect");
// Fail-safe in case the caller doesn't guarantee consistency.
if (!commitHookRegistered_) {
return;
}
uiManager.unregisterCommitHook(*this);
onMutations_ = nullptr;
commitHookRegistered_ = false;
}
void MutationObserverManager::commitHookWasRegistered(
const UIManager& uiManager) noexcept {}
void MutationObserverManager::commitHookWasUnregistered(
const UIManager& uiManager) noexcept {}
RootShadowNode::Unshared MutationObserverManager::shadowTreeWillCommit(
const ShadowTree& shadowTree,
const RootShadowNode::Shared& oldRootShadowNode,
const RootShadowNode::Unshared& newRootShadowNode,
const ShadowTree::CommitOptions& commitOptions) noexcept {
if (commitOptions.source == ShadowTree::CommitSource::React) {
runMutationObservations(shadowTree, *oldRootShadowNode, *newRootShadowNode);
}
return newRootShadowNode;
}
void MutationObserverManager::runMutationObservations(
const ShadowTree& shadowTree,
const RootShadowNode& oldRootShadowNode,
const RootShadowNode& newRootShadowNode) {
TraceSection s("MutationObserverManager::runMutationObservations");
auto surfaceId = shadowTree.getSurfaceId();
auto observersIt = observersBySurfaceId_.find(surfaceId);
if (observersIt == observersBySurfaceId_.end()) {
return;
}
std::vector<MutationRecord> mutationRecords;
auto& observers = observersIt->second;
for (const auto& [mutationObserverId, observer] : observers) {
observer.recordMutations(
oldRootShadowNode, newRootShadowNode, mutationRecords);
}
if (!mutationRecords.empty()) {
onMutations_(mutationRecords);
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/mounting/ShadowTree.h>
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/uimanager/UIManagerCommitHook.h>
#include <vector>
#include "MutationObserver.h"
namespace facebook::react {
using OnMutations = std::function<void(std::vector<MutationRecord> &)>;
class MutationObserverManager final : public UIManagerCommitHook {
public:
MutationObserverManager();
void observe(
MutationObserverId mutationObserverId,
std::shared_ptr<const ShadowNode> shadowNode,
bool observeSubtree,
const UIManager &uiManager);
void unobserveAll(MutationObserverId mutationObserverId);
void connect(UIManager &uiManager, OnMutations &&onMutations);
void disconnect(UIManager &uiManager);
#pragma mark - UIManagerCommitHook
void commitHookWasRegistered(const UIManager &uiManager) noexcept override;
void commitHookWasUnregistered(const UIManager &uiManager) noexcept override;
RootShadowNode::Unshared shadowTreeWillCommit(
const ShadowTree &shadowTree,
const RootShadowNode::Shared &oldRootShadowNode,
const RootShadowNode::Unshared &newRootShadowNode,
const ShadowTree::CommitOptions &commitOptions) noexcept override;
private:
std::unordered_map<SurfaceId, std::unordered_map<MutationObserverId, MutationObserver>> observersBySurfaceId_;
OnMutations onMutations_;
bool commitHookRegistered_{};
void runMutationObservations(
const ShadowTree &shadowTree,
const RootShadowNode &oldRootShadowNode,
const RootShadowNode &newRootShadowNode);
};
} // namespace facebook::react