/* * 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 #include #include namespace facebook::react { using Status = SurfaceHandler::Status; SurfaceHandler::SurfaceHandler( std::string const &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( ContextContainer::Shared 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 { SystraceSection 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{}; { SystraceSection s2("SurfaceHandler::start::paramsLock"); std::shared_lock parametersLock(parametersMutex_); parameters = parameters_; } auto shadowTree = std::make_unique( parameters.surfaceId, parameters.layoutConstraints, parameters.layoutContext, *link_.uiManager, *parameters.contextContainer); link_.shadowTree = shadowTree.get(); link_.uiManager->startSurface( std::move(shadowTree), parameters.moduleName, parameters.props, parameters_.displayMode); 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."); shadowTree->commitEmptyTree(); } void SurfaceHandler::setDisplayMode(DisplayMode displayMode) const noexcept { { std::unique_lock lock(parametersMutex_); if (parameters_.displayMode == displayMode) { return; } parameters_.displayMode = displayMode; } { 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(folly::dynamic const &props) const noexcept { SystraceSection s("SurfaceHandler::setProps"); std::unique_lock lock(parametersMutex_); parameters_.props = props; } folly::dynamic SurfaceHandler::getProps() const noexcept { std::shared_lock lock(parametersMutex_); return parameters_.props; } std::shared_ptr 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( LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext) const noexcept { std::shared_lock lock(linkMutex_); if (link_.status != Status::Running) { return layoutConstraints.clamp({0, 0}); } react_native_assert( link_.shadowTree && "`link_.shadowTree` must not be null."); auto currentRootShadowNode = link_.shadowTree->getCurrentRevision().rootShadowNode; PropsParserContext propsParserContext{ parameters_.surfaceId, *parameters_.contextContainer.get()}; auto rootShadowNode = currentRootShadowNode->clone( propsParserContext, layoutConstraints, layoutContext); rootShadowNode->layoutIfNeeded(); return rootShadowNode->getLayoutMetrics().frame.size; } void SurfaceHandler::constraintLayout( LayoutConstraints const &layoutConstraints, LayoutContext const &layoutContext) const noexcept { SystraceSection 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.get()}; react_native_assert( link_.shadowTree && "`link_.shadowTree` must not be null."); link_.shadowTree->commit( [&](RootShadowNode const &oldRootShadowNode) { return oldRootShadowNode.clone( propsParserContext, layoutConstraints, layoutContext); }, {/* 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 noexcept { SystraceSection 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( [&](RootShadowNode const & /*oldRootShadowNode*/) { return std::static_pointer_cast( revision.rootShadowNode->ShadowNode::clone({})); }, {/* default commit options */}); break; } } void SurfaceHandler::setUIManager(UIManager const *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 { // TODO(T88046056): Fix Android memory leak before uncommenting changes // react_native_assert( // link_.status == Status::Unregistered && // "`SurfaceHandler` must be unregistered (or moved-from) before // deallocation."); } } // namespace facebook::react