diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index 4c9883b068b..2ff70c92659 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -330,13 +331,40 @@ ShadowTreeRegistry const &UIManager::getShadowTreeRegistry() const { return shadowTreeRegistry_; } +void UIManager::registerCommitHook( + UIManagerCommitHook const &commitHook) const { + std::unique_lock lock(commitHookMutex_); + assert( + std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook) == + commitHooks_.end()); + commitHook.commitHookWasRegistered(*this); + commitHooks_.push_back(&commitHook); +} + +void UIManager::unregisterCommitHook( + UIManagerCommitHook const &commitHook) const { + std::unique_lock lock(commitHookMutex_); + auto iterator = + std::find(commitHooks_.begin(), commitHooks_.end(), &commitHook); + assert(iterator != commitHooks_.end()); + commitHooks_.erase(iterator); + commitHook.commitHookWasUnregistered(*this); +} + #pragma mark - ShadowTreeDelegate RootShadowNode::Unshared UIManager::shadowTreeWillCommit( ShadowTree const &shadowTree, RootShadowNode::Shared const &oldRootShadowNode, RootShadowNode::Unshared const &newRootShadowNode) const { + std::shared_lock lock(commitHookMutex_); + auto resultRootShadowNode = newRootShadowNode; + for (auto const *commitHook : commitHooks_) { + resultRootShadowNode = commitHook->shadowTreeWillCommit( + shadowTree, oldRootShadowNode, resultRootShadowNode); + } + return resultRootShadowNode; } diff --git a/ReactCommon/react/renderer/uimanager/UIManager.h b/ReactCommon/react/renderer/uimanager/UIManager.h index 07073f4750e..e138a6ec6e6 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/ReactCommon/react/renderer/uimanager/UIManager.h @@ -25,6 +25,7 @@ namespace facebook { namespace react { class UIManagerBinding; +class UIManagerCommitHook; class UIManager final : public ShadowTreeDelegate { public: @@ -67,6 +68,12 @@ class UIManager final : public ShadowTreeDelegate { std::function callback) const; + /* + * Registers and unregisters a commit hook. + */ + void registerCommitHook(UIManagerCommitHook const &commitHook) const; + void unregisterCommitHook(UIManagerCommitHook const &commitHook) const; + #pragma mark - ShadowTreeDelegate void shadowTreeDidFinishTransaction( @@ -167,6 +174,9 @@ class UIManager final : public ShadowTreeDelegate { // determine whether a commit should be cancelled. Only to be used // inside UIManagerBinding. std::atomic_uint_fast8_t completeRootEventCounter_{0}; + + mutable better::shared_mutex commitHookMutex_; + mutable std::vector commitHooks_; }; } // namespace react diff --git a/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h b/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h new file mode 100644 index 00000000000..c074fffa1e8 --- /dev/null +++ b/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace facebook { +namespace react { + +class ShadowTree; +class UIManager; + +/* + * Implementing a commit hook allows to observe and alter Shadow Tree commits. + */ +class UIManagerCommitHook { + public: + /* + * Called right after the commit hook is registered or unregistered. + */ + virtual void commitHookWasRegistered(UIManager const &uiManager) const + noexcept = 0; + virtual void commitHookWasUnregistered(UIManager const &uiManager) const + noexcept = 0; + + /* + * Called right before a `ShadowTree` commits a new tree. + * The semantic of the method corresponds to a method of the same name + * from `ShadowTreeDelegate`. + */ + virtual RootShadowNode::Unshared shadowTreeWillCommit( + ShadowTree const &shadowTree, + RootShadowNode::Shared const &oldRootShadowNode, + RootShadowNode::Unshared const &newRootShadowNode) const noexcept = 0; + + virtual ~UIManagerCommitHook() noexcept = default; +}; + +} // namespace react +} // namespace facebook