/* * 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 "NativeMutationObserver.h" #include #include #include #include #include #include "Plugins.h" std::shared_ptr NativeMutationObserverModuleProvider( std::shared_ptr jsInvoker) { return std::make_shared( std::move(jsInvoker)); } namespace facebook::react { static UIManager& getUIManagerFromRuntime(jsi::Runtime& runtime) { return UIManagerBinding::getBinding(runtime)->getUIManager(); } NativeMutationObserver::NativeMutationObserver( std::shared_ptr jsInvoker) : NativeMutationObserverCxxSpec(std::move(jsInvoker)) {} void NativeMutationObserver::observe( jsi::Runtime& runtime, NativeMutationObserverObserveOptions options) { auto mutationObserverId = options.mutationObserverId; auto subtree = options.subtree; auto shadowNode = shadowNodeFromValue(runtime, std::move(options).targetShadowNode); auto& uiManager = getUIManagerFromRuntime(runtime); mutationObserverManager_.observe( mutationObserverId, shadowNode, subtree, uiManager); } void NativeMutationObserver::unobserve( jsi::Runtime& runtime, MutationObserverId mutationObserverId, jsi::Object targetShadowNode) { auto shadowNode = shadowNodeFromValue(runtime, std::move(targetShadowNode)); mutationObserverManager_.unobserve(mutationObserverId, *shadowNode); } void NativeMutationObserver::connect( jsi::Runtime& runtime, jsi::Function notifyMutationObservers, SyncCallback getPublicInstanceFromInstanceHandle) { auto& uiManager = getUIManagerFromRuntime(runtime); runtime_ = &runtime; notifyMutationObservers_.emplace(std::move(notifyMutationObservers)); getPublicInstanceFromInstanceHandle_.emplace( std::move(getPublicInstanceFromInstanceHandle)); auto onMutationsCallback = [&](std::vector& records) { return onMutations(records); }; mutationObserverManager_.connect(uiManager, std::move(onMutationsCallback)); } void NativeMutationObserver::disconnect(jsi::Runtime& runtime) { auto& uiManager = getUIManagerFromRuntime(runtime); mutationObserverManager_.disconnect(uiManager); runtime_ = nullptr; notifyMutationObservers_.reset(); getPublicInstanceFromInstanceHandle_.reset(); } std::vector NativeMutationObserver::takeRecords( jsi::Runtime& /*runtime*/) { notifiedMutationObservers_ = false; std::vector records; pendingRecords_.swap(records); return records; } jsi::Value NativeMutationObserver::getPublicInstanceFromShadowNode( const ShadowNode& shadowNode) const { auto instanceHandle = shadowNode.getInstanceHandle(*runtime_); if (!instanceHandle.isObject()) { return jsi::Value::null(); } return getPublicInstanceFromInstanceHandle_.value().call( std::move(instanceHandle)); } std::vector NativeMutationObserver::getPublicInstancesFromShadowNodes( const std::vector& shadowNodes) const { std::vector publicInstances; publicInstances.reserve(shadowNodes.size()); for (const auto& shadowNode : shadowNodes) { publicInstances.push_back(getPublicInstanceFromShadowNode(*shadowNode)); } return publicInstances; } void NativeMutationObserver::onMutations(std::vector& records) { SystraceSection s("NativeMutationObserver::onMutations"); for (const auto& record : records) { pendingRecords_.emplace_back(NativeMutationRecord{ record.mutationObserverId, // FIXME(T157129303) Instead of assuming we can call into JS from here, // we should use an API that explicitly indicates it. getPublicInstanceFromShadowNode(*record.targetShadowNode), getPublicInstancesFromShadowNodes(record.addedShadowNodes), getPublicInstancesFromShadowNodes(record.removedShadowNodes)}); } notifyMutationObserversIfNecessary(); } /** * This method allows us to avoid scheduling multiple calls to notify observers * in the JS thread. We schedule one and skip subsequent ones (we just append * the records to the pending list and wait for the scheduled task to consume * all of them). */ void NativeMutationObserver::notifyMutationObserversIfNecessary() { bool dispatchNotification = false; if (!pendingRecords_.empty() && !notifiedMutationObservers_) { notifiedMutationObservers_ = true; dispatchNotification = true; } if (dispatchNotification) { SystraceSection s("NativeMutationObserver::notifyObservers"); if (ReactNativeFeatureFlags::enableBridgelessArchitecture() && !ReactNativeFeatureFlags::disableEventLoopOnBridgeless()) { runtime_->queueMicrotask(notifyMutationObservers_.value()); } else { jsInvoker_->invokeAsync([&](jsi::Runtime& runtime) { // It's possible that the last observer was disconnected before we could // dispatch this notification. if (notifyMutationObservers_) { notifyMutationObservers_.value().call(runtime); } }); } } } } // namespace facebook::react