From fb4e06b1a5d767a5d77c6eb455a2f0382aad62b3 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 18 Dec 2020 15:59:30 -0800 Subject: [PATCH] Fabric: Introducing `UIManagerCommitHook`, unified way to alter commits Summary: The new UIManagerCommitHook can be used to implement commit-atering features without modifying the core. E.g. State Reconciation seems can be implemented as a coomit hook but first we will use it for a new feature called Timeline (see the coming diffs). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D25221312 fbshipit-source-id: dbe41b475bc8b36e1780d81447ab43b32758bdff --- .../react/renderer/uimanager/UIManager.cpp | 28 ++++++++++++ .../react/renderer/uimanager/UIManager.h | 10 +++++ .../renderer/uimanager/UIManagerCommitHook.h | 45 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h 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