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,38 @@
# 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_scheduler_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_renderer_scheduler STATIC ${react_renderer_scheduler_SRC})
target_include_directories(react_renderer_scheduler PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_renderer_scheduler
folly_runtime
glog
jsi
react_debug
react_featureflags
react_performance_cdpmetrics
react_performance_timeline
react_renderer_componentregistry
react_renderer_core
react_renderer_debug
react_renderer_graphics
react_renderer_mounting
react_renderer_observers_events
react_renderer_runtimescheduler
react_renderer_uimanager
react_utils
rrc_root
rrc_view
yoga
)
target_compile_reactnative_options(react_renderer_scheduler PRIVATE)
target_compile_options(react_renderer_scheduler PRIVATE -Wpedantic)

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.
*/
#pragma once
#include <vector>
namespace facebook::react {
struct InspectorData {
std::vector<std::string> hierarchy;
int selectedIndex;
std::string fileName;
int lineNumber;
int columnNumber;
// TODO T97216348: remove folly::dynamic from InspectorData struct
folly::dynamic props;
};
} // namespace facebook::react

View File

@@ -0,0 +1,397 @@
/*
* 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 "Scheduler.h"
#include <glog/logging.h>
#include <jsi/jsi.h>
#include <cxxreact/TraceSection.h>
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
#include <react/renderer/core/EventQueueProcessor.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/mounting/MountingOverrideDelegate.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
namespace facebook::react {
Scheduler::Scheduler(
const SchedulerToolbox& schedulerToolbox,
UIManagerAnimationDelegate* animationDelegate,
SchedulerDelegate* delegate)
: runtimeExecutor_(schedulerToolbox.runtimeExecutor),
contextContainer_(schedulerToolbox.contextContainer) {
// Creating a container for future `EventDispatcher` instance.
eventDispatcher_ = std::make_shared<std::optional<const EventDispatcher>>();
// TODO(T182293888): remove singleton from PerformanceEntryReporter and move
// creation here.
auto performanceEntryReporter = PerformanceEntryReporter::getInstance();
performanceEntryReporter_ = performanceEntryReporter;
if (ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
ReactNativeFeatureFlags::cdpInteractionMetricsEnabled()) {
cdpMetricsReporter_.emplace(CdpMetricsReporter{runtimeExecutor_});
performanceEntryReporter_->addEventListener(&*cdpMetricsReporter_);
}
if (ReactNativeFeatureFlags::perfIssuesEnabled()) {
cdpPerfIssuesReporter_.emplace(CdpPerfIssuesReporter{runtimeExecutor_});
performanceEntryReporter_->addEventListener(&*cdpPerfIssuesReporter_);
}
eventPerformanceLogger_ =
std::make_shared<EventPerformanceLogger>(performanceEntryReporter_);
auto uiManager =
std::make_shared<UIManager>(runtimeExecutor_, contextContainer_);
auto eventOwnerBox = std::make_shared<EventBeat::OwnerBox>();
eventOwnerBox->owner = eventDispatcher_;
auto weakRuntimeScheduler =
contextContainer_->find<std::weak_ptr<RuntimeScheduler>>(
RuntimeSchedulerKey);
react_native_assert(
weakRuntimeScheduler.has_value() &&
"Unexpected state: RuntimeScheduler was not provided.");
runtimeScheduler_ = weakRuntimeScheduler.value().lock().get();
runtimeScheduler_->setShadowTreeRevisionConsistencyManager(
uiManager->getShadowTreeRevisionConsistencyManager());
runtimeScheduler_->setEventTimingDelegate(eventPerformanceLogger_.get());
auto eventPipe = [uiManager](
jsi::Runtime& runtime,
const EventTarget* eventTarget,
const std::string& type,
ReactEventPriority priority,
const EventPayload& payload) {
uiManager->visitBinding(
[&](const UIManagerBinding& uiManagerBinding) {
uiManagerBinding.dispatchEvent(
runtime, eventTarget, type, priority, payload);
},
runtime);
};
auto eventPipeConclusion = [runtimeScheduler =
runtimeScheduler_](jsi::Runtime& runtime) {
runtimeScheduler->callExpiredTasks(runtime);
};
auto statePipe = [uiManager](const StateUpdate& stateUpdate) {
uiManager->updateState(stateUpdate);
};
auto eventBeat = schedulerToolbox.eventBeatFactory(std::move(eventOwnerBox));
// Creating an `EventDispatcher` instance inside the already allocated
// container (inside the optional).
eventDispatcher_->emplace(
EventQueueProcessor(
eventPipe, eventPipeConclusion, statePipe, eventPerformanceLogger_),
std::move(eventBeat),
statePipe,
eventPerformanceLogger_);
// Casting to `std::shared_ptr<EventDispatcher const>`.
auto eventDispatcher =
EventDispatcher::Shared{eventDispatcher_, &eventDispatcher_->value()};
componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory(
eventDispatcher, contextContainer_);
uiManager->setDelegate(this);
uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_);
auto bindingsExecutor =
schedulerToolbox.bridgelessBindingsExecutor.has_value()
? schedulerToolbox.bridgelessBindingsExecutor.value()
: runtimeExecutor_;
bindingsExecutor([uiManager](jsi::Runtime& runtime) {
UIManagerBinding::createAndInstallIfNeeded(runtime, uiManager);
});
auto componentDescriptorRegistryKey =
"ComponentDescriptorRegistry_DO_NOT_USE_PRETTY_PLEASE";
contextContainer_->erase(componentDescriptorRegistryKey);
contextContainer_->insert(
componentDescriptorRegistryKey,
std::weak_ptr<const ComponentDescriptorRegistry>(
componentDescriptorRegistry_));
delegate_ = delegate;
commitHooks_ = schedulerToolbox.commitHooks;
uiManager_ = uiManager;
for (auto& commitHook : commitHooks_) {
uiManager->registerCommitHook(*commitHook);
}
if (animationDelegate != nullptr) {
animationDelegate->setComponentDescriptorRegistry(
componentDescriptorRegistry_);
}
uiManager_->setAnimationDelegate(animationDelegate);
uiManager->registerMountHook(*eventPerformanceLogger_);
}
Scheduler::~Scheduler() {
LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this
<< ").";
auto weakRuntimeScheduler =
contextContainer_->find<std::weak_ptr<RuntimeScheduler>>(
RuntimeSchedulerKey);
auto runtimeScheduler = weakRuntimeScheduler.has_value()
? weakRuntimeScheduler.value().lock()
: nullptr;
if (runtimeScheduler) {
runtimeScheduler->setEventTimingDelegate(nullptr);
}
for (auto& commitHook : commitHooks_) {
uiManager_->unregisterCommitHook(*commitHook);
}
// All Surfaces must be explicitly stopped before destroying `Scheduler`.
// The idea is that `UIManager` is allowed to call `Scheduler` only if the
// corresponding `ShadowTree` instance exists.
// The thread-safety of this operation is guaranteed by this requirement.
uiManager_->setDelegate(nullptr);
uiManager_->setAnimationDelegate(nullptr);
if (cdpMetricsReporter_) {
performanceEntryReporter_->removeEventListener(&*cdpMetricsReporter_);
}
if (cdpPerfIssuesReporter_) {
performanceEntryReporter_->removeEventListener(&*cdpPerfIssuesReporter_);
}
// Then, let's verify that the requirement was satisfied.
auto surfaceIds = std::vector<SurfaceId>{};
uiManager_->getShadowTreeRegistry().enumerate(
[&surfaceIds](const ShadowTree& shadowTree, bool&) {
surfaceIds.push_back(shadowTree.getSurfaceId());
});
react_native_assert(
surfaceIds.empty() &&
"Scheduler was destroyed with outstanding Surfaces.");
if (surfaceIds.empty()) {
return;
}
LOG(ERROR) << "Scheduler was destroyed with outstanding Surfaces.";
// If we are here, that means assert didn't fire which indicates that we in
// production.
// Now we have still-running surfaces, which is no good, no good.
// That's indeed a sign of a severe issue on the application layer.
// At this point, we don't have much to lose, so we are trying to unmount all
// outstanding `ShadowTree`s to prevent all stored JSI entities from
// overliving the `Scheduler`. (Unmounting `ShadowNode`s disables
// `EventEmitter`s which destroys JSI objects.)
for (auto surfaceId : surfaceIds) {
uiManager_->getShadowTreeRegistry().visit(
surfaceId,
[](const ShadowTree& shadowTree) { shadowTree.commitEmptyTree(); });
// Removing surfaces acquires mutex waiting for commits in flight; in
// theory, it can deadlock.
uiManager_->getShadowTreeRegistry().remove(surfaceId);
}
}
void Scheduler::registerSurface(
const SurfaceHandler& surfaceHandler) const noexcept {
surfaceHandler.setContextContainer(getContextContainer());
surfaceHandler.setUIManager(uiManager_.get());
}
void Scheduler::unregisterSurface(
const SurfaceHandler& surfaceHandler) const noexcept {
surfaceHandler.setUIManager(nullptr);
}
const ComponentDescriptor*
Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
ComponentHandle handle) const {
return componentDescriptorRegistry_
->findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(handle);
}
#pragma mark - Delegate
void Scheduler::setDelegate(SchedulerDelegate* delegate) {
delegate_ = delegate;
}
SchedulerDelegate* Scheduler::getDelegate() const {
return delegate_;
}
#pragma mark - UIManagerAnimationDelegate
void Scheduler::animationTick() const {
uiManager_->animationTick();
}
#pragma mark - UIManagerDelegate
void Scheduler::uiManagerDidFinishTransaction(
std::shared_ptr<const MountingCoordinator> mountingCoordinator,
bool mountSynchronously) {
TraceSection s("Scheduler::uiManagerDidFinishTransaction");
if (delegate_ != nullptr) {
// This is no-op on all platforms except for Android where we need to
// observe each transaction to be able to mount correctly.
delegate_->schedulerDidFinishTransaction(mountingCoordinator);
if (!mountSynchronously) {
auto surfaceId = mountingCoordinator->getSurfaceId();
runtimeScheduler_->scheduleRenderingUpdate(
surfaceId,
[delegate = delegate_,
mountingCoordinator = std::move(mountingCoordinator)]() {
delegate->schedulerShouldRenderTransactions(mountingCoordinator);
});
} else {
delegate_->schedulerShouldRenderTransactions(mountingCoordinator);
}
}
}
void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode& shadowNode) {
if (delegate_ != nullptr) {
delegate_->schedulerDidRequestPreliminaryViewAllocation(shadowNode);
}
}
void Scheduler::uiManagerDidDispatchCommand(
const std::shared_ptr<const ShadowNode>& shadowNode,
const std::string& commandName,
const folly::dynamic& args) {
TraceSection s(
"Scheduler::uiManagerDispatchCommand", "commandName", commandName);
if (delegate_ != nullptr) {
auto shadowView = ShadowView(*shadowNode);
runtimeScheduler_->scheduleRenderingUpdate(
shadowNode->getSurfaceId(),
[delegate = delegate_,
shadowView = std::move(shadowView),
commandName,
args]() {
delegate->schedulerDidDispatchCommand(shadowView, commandName, args);
});
}
}
void Scheduler::uiManagerDidSendAccessibilityEvent(
const std::shared_ptr<const ShadowNode>& shadowNode,
const std::string& eventType) {
TraceSection s("Scheduler::uiManagerDidSendAccessibilityEvent");
if (delegate_ != nullptr) {
auto shadowView = ShadowView(*shadowNode);
delegate_->schedulerDidSendAccessibilityEvent(shadowView, eventType);
}
}
/*
* Set JS responder for a view.
*/
void Scheduler::uiManagerDidSetIsJSResponder(
const std::shared_ptr<const ShadowNode>& shadowNode,
bool isJSResponder,
bool blockNativeResponder) {
if (delegate_ != nullptr) {
delegate_->schedulerDidSetIsJSResponder(
ShadowView(*shadowNode), isJSResponder, blockNativeResponder);
}
}
void Scheduler::uiManagerShouldSynchronouslyUpdateViewOnUIThread(
Tag tag,
const folly::dynamic& props) {
if (delegate_ != nullptr) {
delegate_->schedulerShouldSynchronouslyUpdateViewOnUIThread(tag, props);
}
}
void Scheduler::uiManagerDidUpdateShadowTree(
const std::unordered_map<Tag, folly::dynamic>& tagToProps) {
if (delegate_ != nullptr) {
delegate_->schedulerDidUpdateShadowTree(tagToProps);
}
}
void Scheduler::uiManagerShouldAddEventListener(
std::shared_ptr<const EventListener> listener) {
addEventListener(listener);
}
void Scheduler::uiManagerShouldRemoveEventListener(
const std::shared_ptr<const EventListener>& listener) {
removeEventListener(listener);
}
void Scheduler::uiManagerDidStartSurface(const ShadowTree& shadowTree) {
std::shared_lock lock(onSurfaceStartCallbackMutex_);
if (onSurfaceStartCallback_) {
onSurfaceStartCallback_(shadowTree);
}
}
void Scheduler::reportMount(SurfaceId surfaceId) const {
uiManager_->reportMount(surfaceId);
}
std::shared_ptr<const ContextContainer> Scheduler::getContextContainer() const {
return contextContainer_;
}
std::shared_ptr<UIManager> Scheduler::getUIManager() const {
return uiManager_;
}
void Scheduler::addEventListener(
std::shared_ptr<const EventListener> listener) {
if (eventDispatcher_->has_value()) {
eventDispatcher_->value().addListener(std::move(listener));
}
}
void Scheduler::removeEventListener(
const std::shared_ptr<const EventListener>& listener) {
if (eventDispatcher_->has_value()) {
eventDispatcher_->value().removeListener(listener);
}
}
void Scheduler::uiManagerShouldSetOnSurfaceStartCallback(
OnSurfaceStartCallback&& callback) {
std::shared_lock lock(onSurfaceStartCallbackMutex_);
onSurfaceStartCallback_ = std::move(callback);
}
} // namespace facebook::react

View File

@@ -0,0 +1,153 @@
/*
* 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 <memory>
#include <ReactCommon/RuntimeExecutor.h>
#include <react/performance/cdpmetrics/CdpMetricsReporter.h>
#include <react/performance/cdpmetrics/CdpPerfIssuesReporter.h>
#include <react/performance/timeline/PerformanceEntryReporter.h>
#include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
#include <react/renderer/core/ComponentDescriptor.h>
#include <react/renderer/core/EventEmitter.h>
#include <react/renderer/core/EventListener.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/mounting/MountingOverrideDelegate.h>
#include <react/renderer/observers/events/EventPerformanceLogger.h>
#include <react/renderer/scheduler/InspectorData.h>
#include <react/renderer/scheduler/SchedulerDelegate.h>
#include <react/renderer/scheduler/SchedulerToolbox.h>
#include <react/renderer/scheduler/SurfaceHandler.h>
#include <react/renderer/uimanager/UIManagerAnimationDelegate.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/UIManagerDelegate.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
/*
* Scheduler coordinates Shadow Tree updates and event flows.
*/
class Scheduler final : public UIManagerDelegate {
public:
Scheduler(
const SchedulerToolbox &schedulerToolbox,
UIManagerAnimationDelegate *animationDelegate,
SchedulerDelegate *delegate);
~Scheduler() override;
#pragma mark - Surface Management
/*
* Registers and unregisters a `SurfaceHandler` object in the `Scheduler`.
* All registered `SurfaceHandler` objects must be unregistered
* (with the same `Scheduler`) before their deallocation.
*/
void registerSurface(const SurfaceHandler &surfaceHandler) const noexcept;
void unregisterSurface(const SurfaceHandler &surfaceHandler) const noexcept;
/*
* This is broken. Please do not use.
* `ComponentDescriptor`s are not designed to be used outside of `UIManager`,
* there is no any guarantees about their lifetime.
*/
const ComponentDescriptor *findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(ComponentHandle handle) const;
#pragma mark - Delegate
/*
* Sets and gets the Scheduler's delegate.
* If you requesting a ComponentDescriptor and unsure that it's there, you are
* doing something wrong.
*/
void setDelegate(SchedulerDelegate *delegate);
SchedulerDelegate *getDelegate() const;
#pragma mark - UIManagerAnimationDelegate
// This is not needed on iOS or any platform that has a "pull" instead of
// "push" MountingCoordinator model. This just tells the delegate an update
// is available and that it should `pullTransaction`; we may want to rename
// this to be more generic and not animation-specific.
void animationTick() const;
#pragma mark - UIManagerDelegate
void uiManagerDidFinishTransaction(
std::shared_ptr<const MountingCoordinator> mountingCoordinator,
bool mountSynchronously) override;
void uiManagerDidCreateShadowNode(const ShadowNode &shadowNode) override;
void uiManagerDidDispatchCommand(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::string &commandName,
const folly::dynamic &args) override;
void uiManagerDidSendAccessibilityEvent(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::string &eventType) override;
void uiManagerDidSetIsJSResponder(
const std::shared_ptr<const ShadowNode> &shadowNode,
bool isJSResponder,
bool blockNativeResponder) override;
void uiManagerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) override;
void uiManagerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) override;
void uiManagerShouldAddEventListener(std::shared_ptr<const EventListener> listener) final;
void uiManagerShouldRemoveEventListener(const std::shared_ptr<const EventListener> &listener) final;
void uiManagerDidStartSurface(const ShadowTree &shadowTree) override;
#pragma mark - ContextContainer
std::shared_ptr<const ContextContainer> getContextContainer() const;
#pragma mark - UIManager
std::shared_ptr<UIManager> getUIManager() const;
void reportMount(SurfaceId surfaceId) const;
#pragma mark - Event listeners
void addEventListener(std::shared_ptr<const EventListener> listener);
void removeEventListener(const std::shared_ptr<const EventListener> &listener);
#pragma mark - Surface start callback
void uiManagerShouldSetOnSurfaceStartCallback(OnSurfaceStartCallback &&callback) override;
private:
friend class SurfaceHandler;
SchedulerDelegate *delegate_;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
RuntimeExecutor runtimeExecutor_;
std::shared_ptr<UIManager> uiManager_;
std::vector<std::shared_ptr<UIManagerCommitHook>> commitHooks_;
/*
* At some point, we have to have an owning shared pointer to something that
* will become an `EventDispatcher` a moment later. That's why we have it as a
* pointer to an optional: we construct the pointer first, share that with
* parts that need to have ownership (and only ownership) of that, and then
* fill the optional.
*/
std::shared_ptr<std::optional<const EventDispatcher>> eventDispatcher_;
std::shared_ptr<PerformanceEntryReporter> performanceEntryReporter_;
std::optional<CdpMetricsReporter> cdpMetricsReporter_;
std::optional<CdpPerfIssuesReporter> cdpPerfIssuesReporter_;
std::shared_ptr<EventPerformanceLogger> eventPerformanceLogger_;
/**
* Hold onto ContextContainer. See SchedulerToolbox.
* Must not be nullptr.
*/
std::shared_ptr<const ContextContainer> contextContainer_;
RuntimeScheduler *runtimeScheduler_{nullptr};
mutable std::shared_mutex onSurfaceStartCallbackMutex_;
OnSurfaceStartCallback onSurfaceStartCallback_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,66 @@
/*
* 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 <memory>
#include <react/renderer/core/ReactPrimitives.h>
#include <react/renderer/mounting/MountingCoordinator.h>
#include <react/renderer/mounting/ShadowView.h>
namespace facebook::react {
/*
* Abstract class for Scheduler's delegate.
*/
class SchedulerDelegate {
public:
/*
* Called right after Scheduler computed (and laid out) a new updated version
* of the tree and calculated a set of mutations which are sufficient
* to construct a new one.
*/
virtual void schedulerDidFinishTransaction(const std::shared_ptr<const MountingCoordinator> &mountingCoordinator) = 0;
/*
* Called when the runtime scheduler decides that one-or-more previously
* finished transactions should now be flushed to the screen (atomically).
*
* This is a separate callback from didFinishTransaction as the Android UI
* mounting layer needs to be able toobserve each created ShadowTree to
* correctly apply changes, due to changes in Props representation.
*/
virtual void schedulerShouldRenderTransactions(
const std::shared_ptr<const MountingCoordinator> &mountingCoordinator) = 0;
/*
* Called right after a new ShadowNode was created.
*/
virtual void schedulerDidRequestPreliminaryViewAllocation(const ShadowNode &shadowNode) = 0;
virtual void schedulerDidDispatchCommand(
const ShadowView &shadowView,
const std::string &commandName,
const folly::dynamic &args) = 0;
virtual void schedulerDidSendAccessibilityEvent(const ShadowView &shadowView, const std::string &eventType) = 0;
/*
* Set JS responder for a view
*/
virtual void
schedulerDidSetIsJSResponder(const ShadowView &shadowView, bool isJSResponder, bool blockNativeResponder) = 0;
virtual void schedulerShouldSynchronouslyUpdateViewOnUIThread(Tag tag, const folly::dynamic &props) = 0;
virtual void schedulerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) = 0;
virtual ~SchedulerDelegate() noexcept = default;
};
} // namespace facebook::react

View File

@@ -0,0 +1,63 @@
/*
* 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 <memory>
#include <ReactCommon/RuntimeExecutor.h>
#include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
#include <react/renderer/core/EventBeat.h>
#include <react/renderer/leakchecker/LeakChecker.h>
#include <react/renderer/uimanager/UIManagerCommitHook.h>
#include <react/renderer/uimanager/primitives.h>
#include <react/utils/ContextContainer.h>
#include <react/utils/RunLoopObserver.h>
namespace facebook::react {
/*
* Contains all external dependencies of Scheduler.
* Copyable.
*/
struct SchedulerToolbox final {
/*
* Represents general purpose DI container for product components/needs.
* Must not be `nullptr`.
*/
std::shared_ptr<const ContextContainer> contextContainer;
/*
* Represents externally managed, lazily available collection of components.
*/
ComponentRegistryFactory componentRegistryFactory;
/*
* Represents running JavaScript VM and associated execution queue.
* Can execute lambdas before main bundle has loaded.
*/
std::optional<RuntimeExecutor> bridgelessBindingsExecutor;
/*
* Represents running JavaScript VM and associated execution queue.
* Executes lambdas after main bundle has loaded.
*/
RuntimeExecutor runtimeExecutor;
/*
* Represent connections with the platform-specific run loops and general
* purpose background queue.
*/
EventBeat::Factory eventBeatFactory;
/*
* A list of `UIManagerCommitHook`s that should be registered in `UIManager`.
*/
std::vector<std::shared_ptr<UIManagerCommitHook>> commitHooks;
};
} // namespace facebook::react

View File

@@ -0,0 +1,423 @@
/*
* 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 "SurfaceHandler.h"
#include <cxxreact/TraceSection.h>
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/uimanager/UIManager.h>
namespace facebook::react {
using Status = SurfaceHandler::Status;
SurfaceHandler::SurfaceHandler(
const std::string& moduleName,
SurfaceId surfaceId) noexcept {
parameters_.moduleName = moduleName;
parameters_.surfaceId = surfaceId;
}
SurfaceHandler::SurfaceHandler(SurfaceHandler&& other) noexcept {
operator=(std::move(other));
}
SurfaceHandler& SurfaceHandler::operator=(SurfaceHandler&& other) noexcept {
std::unique_lock lock1(linkMutex_, std::defer_lock);
std::unique_lock lock2(parametersMutex_, std::defer_lock);
std::unique_lock lock3(other.linkMutex_, std::defer_lock);
std::unique_lock lock4(other.parametersMutex_, std::defer_lock);
std::lock(lock1, lock2, lock3, lock4);
link_ = other.link_;
parameters_ = other.parameters_;
other.link_ = Link{};
other.parameters_ = Parameters{};
other.parameters_.contextContainer = parameters_.contextContainer;
return *this;
}
#pragma mark - Surface Life-Cycle Management
void SurfaceHandler::setContextContainer(
std::shared_ptr<const ContextContainer> contextContainer) const noexcept {
parameters_.contextContainer = std::move(contextContainer);
}
Status SurfaceHandler::getStatus() const noexcept {
std::shared_lock lock(linkMutex_);
return link_.status;
}
void SurfaceHandler::start() const noexcept {
TraceSection s("SurfaceHandler::start");
std::unique_lock lock(linkMutex_);
react_native_assert(
link_.status == Status::Registered && "Surface must be registered.");
react_native_assert(
getLayoutConstraints().layoutDirection != LayoutDirection::Undefined &&
"layoutDirection must be set.");
react_native_assert(
parameters_.contextContainer && "ContextContainer must be set.");
auto parameters = Parameters{};
{
TraceSection s2("SurfaceHandler::start::paramsLock");
std::shared_lock parametersLock(parametersMutex_);
parameters = parameters_;
}
auto shadowTree = std::make_unique<ShadowTree>(
parameters.surfaceId,
parameters.layoutConstraints,
parameters.layoutContext,
*link_.uiManager,
*parameters.contextContainer);
link_.shadowTree = shadowTree.get();
if (!parameters.moduleName.empty()) {
link_.uiManager->startSurface(
std::move(shadowTree),
parameters.moduleName,
parameters.props,
parameters_.displayMode);
} else {
link_.uiManager->startEmptySurface(std::move(shadowTree));
}
link_.status = Status::Running;
applyDisplayMode(parameters.displayMode);
}
void SurfaceHandler::stop() const noexcept {
auto shadowTree = ShadowTree::Unique{};
{
std::unique_lock lock(linkMutex_);
react_native_assert(
link_.status == Status::Running && "Surface must be running.");
link_.status = Status::Registered;
link_.shadowTree = nullptr;
shadowTree = link_.uiManager->stopSurface(parameters_.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).
react_native_assert(shadowTree && "`shadowTree` must not be null.");
if (shadowTree) {
shadowTree->commitEmptyTree();
}
}
void SurfaceHandler::setDisplayMode(DisplayMode displayMode) const noexcept {
auto parameters = Parameters{};
{
std::unique_lock lock(parametersMutex_);
if (parameters_.displayMode == displayMode) {
return;
}
parameters_.displayMode = displayMode;
parameters = parameters_;
}
{
std::shared_lock lock(linkMutex_);
if (link_.status != Status::Running) {
return;
}
link_.uiManager->setSurfaceProps(
parameters.surfaceId,
parameters.moduleName,
parameters.props,
parameters.displayMode);
applyDisplayMode(displayMode);
}
}
DisplayMode SurfaceHandler::getDisplayMode() const noexcept {
std::shared_lock lock(parametersMutex_);
return parameters_.displayMode;
}
#pragma mark - Accessors
SurfaceId SurfaceHandler::getSurfaceId() const noexcept {
std::shared_lock lock(parametersMutex_);
return parameters_.surfaceId;
}
void SurfaceHandler::setSurfaceId(SurfaceId surfaceId) const noexcept {
std::unique_lock lock(parametersMutex_);
parameters_.surfaceId = surfaceId;
}
std::string SurfaceHandler::getModuleName() const noexcept {
std::shared_lock lock(parametersMutex_);
return parameters_.moduleName;
}
void SurfaceHandler::setProps(const folly::dynamic& props) const noexcept {
TraceSection s("SurfaceHandler::setProps");
auto parameters = Parameters{};
{
std::unique_lock lock(parametersMutex_);
parameters_.props = props;
parameters = parameters_;
}
{
std::shared_lock lock(linkMutex_);
if (link_.status == Status::Running) {
link_.uiManager->setSurfaceProps(
parameters.surfaceId,
parameters.moduleName,
parameters.props,
parameters.displayMode);
}
}
}
folly::dynamic SurfaceHandler::getProps() const noexcept {
std::shared_lock lock(parametersMutex_);
return parameters_.props;
}
std::shared_ptr<const MountingCoordinator>
SurfaceHandler::getMountingCoordinator() const noexcept {
std::shared_lock lock(linkMutex_);
react_native_assert(
link_.status != Status::Unregistered && "Surface must be registered.");
react_native_assert(
link_.shadowTree && "`link_.shadowTree` must not be null.");
return link_.shadowTree->getMountingCoordinator();
}
#pragma mark - Layout
Size SurfaceHandler::measure(
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext) const {
std::shared_lock lock(linkMutex_);
if (link_.status != Status::Running) {
return layoutConstraints.clamp({.width = 0, .height = 0});
}
react_native_assert(
link_.shadowTree && "`link_.shadowTree` must not be null.");
auto currentRootShadowNode =
link_.shadowTree->getCurrentRevision().rootShadowNode;
PropsParserContext propsParserContext{
parameters_.surfaceId, *parameters_.contextContainer};
auto rootShadowNode = currentRootShadowNode->clone(
propsParserContext, layoutConstraints, layoutContext);
rootShadowNode->layoutIfNeeded();
return rootShadowNode->getLayoutMetrics().frame.size;
}
std::shared_ptr<const ShadowNode> SurfaceHandler::dirtyMeasurableNodesRecursive(
std::shared_ptr<const ShadowNode> node) const {
const auto nodeHasChildren = !node->getChildren().empty();
const auto isMeasurableYogaNode =
node->getTraits().check(ShadowNodeTraits::Trait::MeasurableYogaNode);
// Node is not measurable and has no children, its layout will not be affected
if (!nodeHasChildren && !isMeasurableYogaNode) {
return nullptr;
}
ShadowNode::SharedListOfShared newChildren =
ShadowNodeFragment::childrenPlaceholder();
if (nodeHasChildren) {
std::shared_ptr<std::vector<std::shared_ptr<const ShadowNode>>>
newChildrenMutable = nullptr;
for (size_t i = 0; i < node->getChildren().size(); i++) {
const auto& child = node->getChildren()[i];
if (const auto& layoutableNode =
std::dynamic_pointer_cast<const YogaLayoutableShadowNode>(
child)) {
auto newChild = dirtyMeasurableNodesRecursive(layoutableNode);
if (newChild != nullptr) {
if (newChildrenMutable == nullptr) {
newChildrenMutable = std::make_shared<
std::vector<std::shared_ptr<const ShadowNode>>>(
node->getChildren());
newChildren = newChildrenMutable;
}
(*newChildrenMutable)[i] = newChild;
}
}
}
// Node is not measurable and its children were not dirtied, its layout will
// not be affected
if (!isMeasurableYogaNode && newChildrenMutable == nullptr) {
return nullptr;
}
}
const auto newNode = node->getComponentDescriptor().cloneShadowNode(
*node,
{
.children = newChildren,
// Preserve the original state of the node
.state = node->getState(),
});
if (isMeasurableYogaNode) {
std::static_pointer_cast<YogaLayoutableShadowNode>(newNode)->dirtyLayout();
}
return newNode;
}
void SurfaceHandler::dirtyMeasurableNodes(ShadowNode& root) const {
for (const auto& child : root.getChildren()) {
if (const auto& layoutableNode =
std::dynamic_pointer_cast<const YogaLayoutableShadowNode>(child)) {
const auto newChild = dirtyMeasurableNodesRecursive(layoutableNode);
if (newChild != nullptr) {
root.replaceChild(*child, newChild);
}
}
}
}
void SurfaceHandler::constraintLayout(
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext) const {
TraceSection s("SurfaceHandler::constraintLayout");
{
std::unique_lock lock(parametersMutex_);
if (parameters_.layoutConstraints == layoutConstraints &&
parameters_.layoutContext == layoutContext) {
return;
}
parameters_.layoutConstraints = layoutConstraints;
parameters_.layoutContext = layoutContext;
}
{
std::shared_lock lock(linkMutex_);
if (link_.status != Status::Running) {
return;
}
PropsParserContext propsParserContext{
parameters_.surfaceId, *parameters_.contextContainer};
react_native_assert(
link_.shadowTree && "`link_.shadowTree` must not be null.");
link_.shadowTree->commit(
[&](const RootShadowNode& oldRootShadowNode) {
auto newRoot = oldRootShadowNode.clone(
propsParserContext, layoutConstraints, layoutContext);
// Dirty all measurable nodes when the fontSizeMultiplier changes to
// trigger re-measurement.
if (ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout() &&
layoutContext.fontSizeMultiplier !=
oldRootShadowNode.getConcreteProps()
.layoutContext.fontSizeMultiplier) {
dirtyMeasurableNodes(*newRoot);
}
return newRoot;
},
{/* default commit options */});
}
}
LayoutConstraints SurfaceHandler::getLayoutConstraints() const noexcept {
std::shared_lock lock(parametersMutex_);
return parameters_.layoutConstraints;
}
LayoutContext SurfaceHandler::getLayoutContext() const noexcept {
std::shared_lock lock(parametersMutex_);
return parameters_.layoutContext;
}
#pragma mark - Private
void SurfaceHandler::applyDisplayMode(DisplayMode displayMode) const {
TraceSection s("SurfaceHandler::applyDisplayMode");
react_native_assert(
link_.status == Status::Running && "Surface must be running.");
react_native_assert(
link_.shadowTree && "`link_.shadowTree` must not be null.");
switch (displayMode) {
case DisplayMode::Visible:
link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal);
break;
case DisplayMode::Suspended:
link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended);
break;
case DisplayMode::Hidden:
link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Normal);
// Getting a current revision.
auto revision = link_.shadowTree->getCurrentRevision();
// Committing an empty tree to force mounting to disassemble view
// hierarchy.
link_.shadowTree->commitEmptyTree();
link_.shadowTree->setCommitMode(ShadowTree::CommitMode::Suspended);
// Committing the current revision back. It will be mounted only when
// `DisplayMode` is changed back to `Normal`.
link_.shadowTree->commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(
revision.rootShadowNode->ShadowNode::clone({}));
},
{/* default commit options */});
break;
}
}
void SurfaceHandler::setUIManager(const UIManager* uiManager) const noexcept {
std::unique_lock lock(linkMutex_);
react_native_assert(
link_.status != Status::Running && "Surface must not be running.");
if (link_.uiManager == uiManager) {
return;
}
link_.uiManager = uiManager;
link_.status =
uiManager != nullptr ? Status::Registered : Status::Unregistered;
}
SurfaceHandler::~SurfaceHandler() noexcept {
react_native_assert(
link_.status == Status::Unregistered &&
"`SurfaceHandler` must be unregistered (or moved-from) before deallocation.");
}
} // namespace facebook::react

View File

@@ -0,0 +1,210 @@
/*
* 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 <memory>
#include <shared_mutex>
#include <folly/dynamic.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/ReactPrimitives.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
class Scheduler;
class ShadowTree;
class MountingCoordinator;
class UIManager;
/*
* Represents a running React Native surface and provides control over it.
* The instances of this class are movable only.
* The instances of this class can be safely deallocated only if `status` is
* `Unregistered`; this is a way to enforce internal consistency and
* deallocation ordering constraints the core relies on.
*
*
* Even though all methods of the class are thread-safe, the consumer side must
* ensure the logical consistency of some methods (e.g. calling `stop` for
* non-running surface will crash).
*/
class SurfaceHandler {
public:
/*
* Represents a status of the `SurfaceHandler` instance.
*/
enum class Status {
/*
* Newly created, moved-from, or already-unregistered instances. The only
* state in which the object can be safely deallocated.
*/
Unregistered = 0,
/*
* Registered instances that have an internal reference to a `UIManager`
* instance and ready to start a surface.
*/
Registered = 1,
/*
* Registered and running instances.
*/
Running = 2,
};
/*
* Can be constructed anytime with a `moduleName` and a `surfaceId`.
*/
SurfaceHandler(const std::string &moduleName, SurfaceId surfaceId) noexcept;
virtual ~SurfaceHandler() noexcept;
/*
* Movable-only.
*/
SurfaceHandler(SurfaceHandler &&other) noexcept;
SurfaceHandler(const SurfaceHandler &SurfaceHandler) noexcept = delete;
SurfaceHandler &operator=(SurfaceHandler &&other) noexcept;
SurfaceHandler &operator=(const SurfaceHandler &other) noexcept = delete;
#pragma mark - Surface Life-Cycle Management
/*
* Must be called before surface is started.
*/
void setContextContainer(std::shared_ptr<const ContextContainer> contextContainer) const noexcept;
/*
* Returns a momentum value of the status.
*/
Status getStatus() const noexcept;
/*
* Starts or stops the surface.
* Can not be called when the status is `Unregistered`.
* `start()` must not be called for a running surface, and `stop()` must not
* be called for a not running surface.
*/
void start() const noexcept;
void stop() const noexcept;
/*
* Sets (and gets) the running mode.
* The running mode can be changed anytime (even for `Unregistered` surface).
*/
virtual void setDisplayMode(DisplayMode displayMode) const noexcept;
DisplayMode getDisplayMode() const noexcept;
#pragma mark - Accessors
SurfaceId getSurfaceId() const noexcept;
void setSurfaceId(SurfaceId surfaceId) const noexcept;
std::string getModuleName() const noexcept;
/*
* Provides access for surface props.
* Props can be changed anytime (even for `Unregistered` surface).
*/
void setProps(const folly::dynamic &props) const noexcept;
folly::dynamic getProps() const noexcept;
/*
* Returns a `MountingCoordinator` instance associated with a running surface.
* Can be not be called when the status is `Unregistered`.
* The returning value cannot be `nullptr`.
*/
std::shared_ptr<const MountingCoordinator> getMountingCoordinator() const noexcept;
#pragma mark - Layout
/*
* Measures the surface with given layout constraints and layout context.
* Returns zero size if called on the stopped or unregistered surface.
*/
Size measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
/*
* Sets layout constraints and layout context for the surface.
*/
void constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
/*
* Returns layout constraints and layout context associated with the surface.
*/
LayoutConstraints getLayoutConstraints() const noexcept;
LayoutContext getLayoutContext() const noexcept;
private:
friend class Scheduler;
/*
* Must be called by `Scheduler` during registration process.
*/
void setUIManager(const UIManager *uiManager) const noexcept;
void applyDisplayMode(DisplayMode displayMode) const;
/*
* An utility for dirtying all measurable shadow nodes present in the tree.
*/
void dirtyMeasurableNodes(ShadowNode &root) const;
std::shared_ptr<const ShadowNode> dirtyMeasurableNodesRecursive(std::shared_ptr<const ShadowNode> node) const;
#pragma mark - Link & Parameters
/*
* All data members of the class are split into two groups (`Link` and
* `Parameters`) that require separate synchronization. This way it's easier
* to see that proper lock is acquired. Separate synchronization is needed to
* prevent deadlocks.
*/
/*
* Represents parameters of the surface. Parameters can be changed
* independently from controlling the running state
* (registering/unregistering, starting/stopping) of the surface.
* Changing parameters requires acquiring a unique lock; reading needs only
* a shared lock.
*/
struct Parameters {
std::string moduleName{};
SurfaceId surfaceId{};
DisplayMode displayMode{DisplayMode::Visible};
folly::dynamic props{};
LayoutConstraints layoutConstraints{};
LayoutContext layoutContext{};
std::shared_ptr<const ContextContainer> contextContainer{};
};
/*
* Represents an underlying link to a `ShadowTree` and an `UIManager`.
* Registering, unregistering, starting, and stopping the surface requires
* acquiring a unique lock; other access needs only a shared lock.
*/
struct Link {
Status status{Status::Unregistered};
const UIManager *uiManager{};
const ShadowTree *shadowTree{};
};
/*
* `link_` and `linkMutex_` pair.
*/
mutable std::shared_mutex linkMutex_;
mutable Link link_;
/*
* `parameters_` and `parametersMutex_` pair.
*/
mutable std::shared_mutex parametersMutex_;
mutable Parameters parameters_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,156 @@
/*
* 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 "SurfaceManager.h"
#include <glog/logging.h>
#include <react/renderer/scheduler/Scheduler.h>
namespace facebook::react {
SurfaceManager::SurfaceManager(const Scheduler& scheduler) noexcept
: scheduler_(scheduler) {}
SurfaceManager::~SurfaceManager() noexcept {
LOG(WARNING) << "SurfaceManager::~SurfaceManager() was called (address: "
<< this << ").";
stopAllSurfaces();
}
void SurfaceManager::startSurface(
SurfaceId surfaceId,
const std::string& moduleName,
const folly::dynamic& props,
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext) noexcept {
{
std::unique_lock lock(mutex_);
auto surfaceHandler = SurfaceHandler{moduleName, surfaceId};
surfaceHandler.setContextContainer(scheduler_.getContextContainer());
registry_.emplace(surfaceId, std::move(surfaceHandler));
}
visit(surfaceId, [&](const SurfaceHandler& surfaceHandler) {
surfaceHandler.setProps(props);
surfaceHandler.constraintLayout(layoutConstraints, layoutContext);
scheduler_.registerSurface(surfaceHandler);
surfaceHandler.start();
});
}
void SurfaceManager::stopSurface(SurfaceId surfaceId) noexcept {
bool surfaceWasRunning = false;
visit(surfaceId, [&](const SurfaceHandler& surfaceHandler) {
surfaceHandler.stop();
scheduler_.unregisterSurface(surfaceHandler);
surfaceWasRunning = true;
});
if (!surfaceWasRunning) {
LOG(WARNING)
<< "SurfaceManager::stopSurface tried to stop a surface which was not running, surfaceId = "
<< surfaceId;
}
{
std::unique_lock lock(mutex_);
auto iterator = registry_.find(surfaceId);
registry_.erase(iterator);
}
}
void SurfaceManager::stopAllSurfaces() noexcept {
auto surfaceIds = getRunningSurfaces();
for (const auto& surfaceId : surfaceIds) {
stopSurface(surfaceId);
}
}
bool SurfaceManager::isSurfaceRunning(SurfaceId surfaceId) const noexcept {
std::shared_lock lock(mutex_);
return registry_.contains(surfaceId);
}
std::unordered_set<SurfaceId> SurfaceManager::getRunningSurfaces()
const noexcept {
std::unordered_set<SurfaceId> surfaceIds;
{
std::shared_lock lock(mutex_);
for (const auto& [surfaceId, _] : registry_) {
surfaceIds.insert(surfaceId);
}
}
return surfaceIds;
}
std::optional<SurfaceManager::SurfaceProps> SurfaceManager::getSurfaceProps(
SurfaceId surfaceId) const noexcept {
std::optional<SurfaceManager::SurfaceProps> surfaceProps;
visit(surfaceId, [&](const SurfaceHandler& surfaceHandler) {
surfaceProps = SurfaceManager::SurfaceProps{
.surfaceId = surfaceId,
.moduleName = surfaceHandler.getModuleName(),
.props = surfaceHandler.getProps(),
.layoutConstraints = surfaceHandler.getLayoutConstraints(),
.layoutContext = surfaceHandler.getLayoutContext()};
});
return surfaceProps;
}
Size SurfaceManager::measureSurface(
SurfaceId surfaceId,
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext) const noexcept {
auto size = Size{};
visit(surfaceId, [&](const SurfaceHandler& surfaceHandler) {
size = surfaceHandler.measure(layoutConstraints, layoutContext);
});
return size;
}
std::shared_ptr<const MountingCoordinator>
SurfaceManager::findMountingCoordinator(SurfaceId surfaceId) const noexcept {
auto mountingCoordinator = std::shared_ptr<const MountingCoordinator>{};
visit(surfaceId, [&](const SurfaceHandler& surfaceHandler) {
mountingCoordinator = surfaceHandler.getMountingCoordinator();
});
return mountingCoordinator;
}
void SurfaceManager::constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints& layoutConstraints,
const LayoutContext& layoutContext) const noexcept {
visit(surfaceId, [=](const SurfaceHandler& surfaceHandler) {
surfaceHandler.constraintLayout(layoutConstraints, layoutContext);
});
}
void SurfaceManager::visit(
SurfaceId surfaceId,
const std::function<void(const SurfaceHandler& surfaceHandler)>& callback)
const noexcept {
std::shared_lock lock(mutex_);
auto iterator = registry_.find(surfaceId);
if (iterator == registry_.end()) {
return;
}
callback(iterator->second);
}
} // namespace facebook::react

View File

@@ -0,0 +1,82 @@
/*
* 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 <mutex>
#include <optional>
#include <shared_mutex>
#include <unordered_map>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/mounting/MountingCoordinator.h>
#include <react/renderer/scheduler/SurfaceHandler.h>
namespace facebook::react {
/*
* `SurfaceManager` allows controlling React Native Surfaces via
* `SurfaceHandler` without using `SurfaceHandler` directly. `SurfaceManager`
* maintains a registry of `SurfaceHandler`s and allows to reference to them via
* a `SurfaceId`.
* The is supposed to be used during the transition period only.
*/
class SurfaceManager final {
public:
explicit SurfaceManager(const Scheduler &scheduler) noexcept;
~SurfaceManager() noexcept;
/* SurfaceProps contain information about running surfaces */
struct SurfaceProps {
SurfaceId surfaceId;
std::string moduleName;
folly::dynamic props;
LayoutConstraints layoutConstraints;
LayoutContext layoutContext;
};
#pragma mark - Surface Management
void startSurface(
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &props,
const LayoutConstraints &layoutConstraints = {},
const LayoutContext &layoutContext = {}) noexcept;
void stopSurface(SurfaceId surfaceId) noexcept;
void stopAllSurfaces() noexcept;
bool isSurfaceRunning(SurfaceId surfaceId) const noexcept;
std::unordered_set<SurfaceId> getRunningSurfaces() const noexcept;
std::optional<SurfaceManager::SurfaceProps> getSurfaceProps(SurfaceId surfaceId) const noexcept;
Size measureSurface(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const noexcept;
void constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const noexcept;
std::shared_ptr<const MountingCoordinator> findMountingCoordinator(SurfaceId surfaceId) const noexcept;
private:
void visit(SurfaceId surfaceId, const std::function<void(const SurfaceHandler &surfaceHandler)> &callback)
const noexcept;
const Scheduler &scheduler_;
mutable std::shared_mutex mutex_; // Protects `registry_`.
std::unordered_map<SurfaceId, SurfaceHandler> registry_{};
};
} // namespace facebook::react