diff --git a/packages/react-native/React/Fabric/AppleEventBeat.cpp b/packages/react-native/React/Fabric/AppleEventBeat.cpp deleted file mode 100644 index 4a3d533a0cd9..000000000000 --- a/packages/react-native/React/Fabric/AppleEventBeat.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 "AppleEventBeat.h" - -#include - -namespace facebook::react { - -AppleEventBeat::AppleEventBeat( - std::shared_ptr ownerBox, - std::unique_ptr uiRunLoopObserver, - RuntimeScheduler& runtimeScheduler) - : EventBeat(std::move(ownerBox), runtimeScheduler), - uiRunLoopObserver_(std::move(uiRunLoopObserver)) { - uiRunLoopObserver_->setDelegate(this); - uiRunLoopObserver_->enable(); -} - -void AppleEventBeat::activityDidChange( - const RunLoopObserver::Delegate* delegate, - RunLoopObserver::Activity /*activity*/) const noexcept { - react_native_assert(delegate == this); - induce(); -} - -} // namespace facebook::react diff --git a/packages/react-native/React/Fabric/AppleEventBeat.h b/packages/react-native/React/Fabric/AppleEventBeat.h index 256e0f0983ad..4124fc7860aa 100644 --- a/packages/react-native/React/Fabric/AppleEventBeat.h +++ b/packages/react-native/React/Fabric/AppleEventBeat.h @@ -7,10 +7,18 @@ #pragma once +#include +#include +#include + +#import + #include #include #include +@class RCTEventBeatFlusherLayer; + namespace facebook::react { class RuntimeScheduler; @@ -19,13 +27,34 @@ class RuntimeScheduler; * Event beat associated with JavaScript runtime. * The beat is called on `RuntimeExecutor`'s thread induced by the UI thread * event loop. + * + * A synchronous request made while Core Animation is laying out the current + * frame (the run loop observer that induces the beat has already run at that + * point) is additionally induced from the display phase of the same commit + * cycle, so that its effects are mounted before the frame is presented. The + * induce is scheduled on the layer of the requesting view's window — the root + * of the tree Core Animation is laying out when the request is made from + * layout. */ class AppleEventBeat : public EventBeat, public RunLoopObserver::Delegate { public: + /* + * Resolves the layer of the window containing the view with the given tag. + * Called on the main thread; returns nil when the view is not mounted or + * not attached to a window. + */ + using WindowLayerResolver = std::function; + AppleEventBeat( std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, - RuntimeScheduler &RuntimeScheduler); + RuntimeScheduler &RuntimeScheduler, + WindowLayerResolver windowLayerResolver); + + ~AppleEventBeat() override; + + using EventBeat::requestSynchronous; + void requestSynchronous(Tag tag) const override; #pragma mark - RunLoopObserver::Delegate @@ -34,6 +63,9 @@ class AppleEventBeat : public EventBeat, public RunLoopObserver::Delegate { private: std::unique_ptr uiRunLoopObserver_; + WindowLayerResolver windowLayerResolver_; + NSMapTable *layers_; + void (^onDisplay_)(void); }; } // namespace facebook::react diff --git a/packages/react-native/React/Fabric/AppleEventBeat.mm b/packages/react-native/React/Fabric/AppleEventBeat.mm new file mode 100644 index 000000000000..1086c9bc40b8 --- /dev/null +++ b/packages/react-native/React/Fabric/AppleEventBeat.mm @@ -0,0 +1,122 @@ +/* + * 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 "AppleEventBeat.h" + +#import +#import + +#include + +/* + * A zero-sized layer whose only purpose is to run a callback during the + * display phase of a Core Animation commit. Core Animation processes a commit + * as layout → display → (repeat until stable) → commit, so a layer marked as + * needing display during the layout phase has its `display` called after the + * whole layout pass but before the transaction is committed. + */ +@interface RCTEventBeatFlusherLayer : CALayer +- (instancetype)initWithOnDisplay:(void (^)(void))onDisplay; +@end + +@implementation RCTEventBeatFlusherLayer { + void (^_onDisplay)(void); +} + +- (instancetype)initWithOnDisplay:(void (^)(void))onDisplay +{ + if (self = [super init]) { + _onDisplay = [onDisplay copy]; + self.frame = CGRectZero; + } + return self; +} + +- (void)display +{ + _onDisplay(); +} + +// The layer is not a visual element; never participate in animations. +- (id)actionForKey:(NSString *)event +{ + return nil; +} + +@end + +namespace facebook::react { + +AppleEventBeat::AppleEventBeat(std::shared_ptr ownerBox, + std::unique_ptr uiRunLoopObserver, + RuntimeScheduler &runtimeScheduler, + WindowLayerResolver windowLayerResolver) + : EventBeat(std::move(ownerBox), runtimeScheduler), + uiRunLoopObserver_(std::move(uiRunLoopObserver)), + windowLayerResolver_(std::move(windowLayerResolver)), + layers_([NSMapTable weakToStrongObjectsMapTable]) +{ + std::weak_ptr weakOwner = ownerBox_->owner; + onDisplay_ = ^{ + // The owner (indirectly) retains the event beat; if it is gone, so is + // the beat this induces. + auto owner = weakOwner.lock(); + if (!owner) { + return; + } + this->induce(); + }; + + uiRunLoopObserver_->setDelegate(this); + uiRunLoopObserver_->enable(); +} + +AppleEventBeat::~AppleEventBeat() +{ + // The beat can be destroyed on any thread; layer mutations belong on the + // main thread. The block only retains the layers, and a display happening + // before it executes is made safe by the owner check above. + NSMapTable *layers = layers_; + RCTExecuteOnMainQueue(^{ + for (RCTEventBeatFlusherLayer *layer in layers.objectEnumerator) { + [layer removeFromSuperlayer]; + } + [layers removeAllObjects]; + }); +} + +void AppleEventBeat::requestSynchronous(Tag tag) const +{ + EventBeat::requestSynchronous(tag); + + if (tag == kNoTag || !RCTIsMainQueue()) { + return; + } + CALayer *hostLayer = windowLayerResolver_ ? windowLayerResolver_(tag) : nil; + if (hostLayer == nil) { + return; + } + RCTEventBeatFlusherLayer *layer = [layers_ objectForKey:hostLayer]; + if (layer == nil) { + layer = [[RCTEventBeatFlusherLayer alloc] initWithOnDisplay:onDisplay_]; + [layers_ setObject:layer forKey:hostLayer]; + } + if (layer.superlayer != hostLayer) { + [layer removeFromSuperlayer]; + [hostLayer addSublayer:layer]; + } + [layer setNeedsDisplay]; +} + +void AppleEventBeat::activityDidChange(const RunLoopObserver::Delegate *delegate, + RunLoopObserver::Activity /*activity*/) const noexcept +{ + react_native_assert(delegate == this); + induce(); +} + +} // namespace facebook::react diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm index 0e4bbe376463..fa91a0896c50 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm @@ -292,11 +292,16 @@ - (RCTScheduler *)_createScheduler toolbox.runtimeExecutor = runtimeExecutor; toolbox.bridgelessBindingsExecutor = _bridgelessBindingsExecutor; - toolbox.eventBeatFactory = - [runtimeScheduler](std::shared_ptr ownerBox) -> std::unique_ptr { + RCTMountingManager *mountingManager = _mountingManager; + toolbox.eventBeatFactory = [runtimeScheduler, + mountingManager](std::shared_ptr ownerBox) -> std::unique_ptr { auto runLoopObserver = std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); - return std::make_unique(std::move(ownerBox), std::move(runLoopObserver), *runtimeScheduler); + auto windowLayerResolver = [mountingManager](Tag tag) -> CALayer * { + return [mountingManager.componentViewRegistry findComponentViewWithTag:tag].window.layer; + }; + return std::make_unique( + std::move(ownerBox), std::move(runLoopObserver), *runtimeScheduler, std::move(windowLayerResolver)); }; RCTScheduler *scheduler = [[RCTScheduler alloc] initWithToolbox:toolbox]; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.cpp index 48557476cf0c..4b1ff61fe4bc 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.cpp @@ -79,7 +79,7 @@ CppMountItem CppMountItem::UpdateEventEmitterMountItem( const ShadowView& shadowView) { return { .type = CppMountItem::Type::UpdateEventEmitter, - .parentTag = -1, + .parentTag = kNoTag, .oldChildShadowView = {}, .newChildShadowView = shadowView, .index = -1}; @@ -88,7 +88,7 @@ CppMountItem CppMountItem::UpdatePaddingMountItem( const ShadowView& shadowView) { return { .type = CppMountItem::Type::UpdatePadding, - .parentTag = -1, + .parentTag = kNoTag, .oldChildShadowView = {}, .newChildShadowView = shadowView, .index = -1}; @@ -97,7 +97,7 @@ CppMountItem CppMountItem::UpdateOverflowInsetMountItem( const ShadowView& shadowView) { return { .type = CppMountItem::Type::UpdateOverflowInset, - .parentTag = -1, + .parentTag = kNoTag, .oldChildShadowView = {}, .newChildShadowView = shadowView, .index = -1}; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.h index 3384e4df3b4c..0901de24f001 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/MountItem.h @@ -59,7 +59,7 @@ struct CppMountItem final { #pragma mark - Fields Type type = {Create}; - Tag parentTag = -1; + Tag parentTag = kNoTag; ShadowView oldChildShadowView = {}; ShadowView newChildShadowView = {}; int index = {}; diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp index cdb05f4719fd..e8cf9ffdda29 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp @@ -26,6 +26,10 @@ void EventBeat::request() const { } void EventBeat::requestSynchronous() const { + requestSynchronous(kNoTag); +} + +void EventBeat::requestSynchronous(Tag /*tag*/) const { react_native_assert( beatCallback_ && "Unexpected state: EventBeat::setBeatCallback was not called before EventBeat::requestSynchronous."); @@ -53,7 +57,14 @@ void EventBeat::induce() const { isEventBeatRequested_ = false; if (isBeatCallbackScheduled_) { - return; + // An asynchronous beat is already scheduled but has not run yet. A + // synchronous request must not be stranded behind it (it would silently + // lose its this-frame guarantee, and the leftover flag would make an + // unrelated later beat blocking), so it proceeds and processes the queue + // now; the already scheduled beat will simply find an empty queue. + if (!isSynchronousRequested_) { + return; + } } isBeatCallbackScheduled_ = true; diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h index 6f25d5f45c69..b29096537a12 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -110,8 +111,18 @@ class EventBeat { * thread────────────────────┴─────────────────────────┴▶ * Both JS and UI thread are * blocked. + * + * `tag` is the view the request originates from, or `kNoTag` when unknown. + * Platform implementations use it to schedule an induce where that view + * renders, and fall back to their ordinary beat timing without it. */ - virtual void requestSynchronous() const; + virtual void requestSynchronous(Tag tag) const; + + /* + * Convenience for requesters with no view attribution. + */ + void requestSynchronous() const; + /* * The callback will be executed once a consumer (for example EventQueue) diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp index 5fa5e6821a51..70f9c63ee77e 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp @@ -37,8 +37,8 @@ void EventDispatcher::dispatchEvent(RawEvent&& rawEvent) const { eventQueue_.enqueueEvent(std::move(rawEvent)); } -void EventDispatcher::experimental_flushSync() const { - eventQueue_.experimental_flushSync(); +void EventDispatcher::experimental_flushSync(Tag tag) const { + eventQueue_.experimental_flushSync(tag); } void EventDispatcher::dispatchStateUpdate( diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h index 5aaa48364f9e..65ab667e1a01 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -46,7 +47,7 @@ class EventDispatcher { /* * Experimental API exposed to support EventEmitter::experimental_flushSync. */ - void experimental_flushSync() const; + void experimental_flushSync(Tag tag) const; /* * Dispatches a raw event with asynchronous batched priority. Before the diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.cpp index 2d6ac50e3730..a22692867662 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.cpp @@ -7,6 +7,8 @@ #include "EventEmitter.h" +#include + #include #include #include @@ -229,8 +231,15 @@ void EventEmitter::setEnabled(bool enabled) { } } +Tag EventEmitter::getTag() const { + return tag_; +} + void EventEmitter::setShadowNodeFamily( std::weak_ptr shadowNodeFamily) { + if (auto family = shadowNodeFamily.lock()) { + tag_ = family->getTag(); + } shadowNodeFamily_ = std::move(shadowNodeFamily); } diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.h b/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.h index 8863d9a6f4fe..1f68e044a980 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventEmitter.h @@ -65,6 +65,12 @@ class EventEmitter { const SharedEventTarget &getEventTarget() const; + /* + * The tag of the view this emitter belongs to, or `kNoTag` when none is + * attached. + */ + Tag getTag() const; + /* * Experimental API that will change in the future. */ @@ -77,7 +83,7 @@ class EventEmitter { } syncFunc(); - eventDispatcher->experimental_flushSync(); + eventDispatcher->experimental_flushSync(getTag()); } /* @@ -134,6 +140,7 @@ class EventEmitter { friend class UIManagerBinding; SharedEventTarget eventTarget_; + Tag tag_{kNoTag}; std::weak_ptr shadowNodeFamily_; EventDispatcher::Weak eventDispatcher_; diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp index 6e99fd71bb7a..27b4ee16df91 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.cpp @@ -86,8 +86,8 @@ void EventQueue::onEnqueue() const { eventBeat_->request(); } -void EventQueue::experimental_flushSync() const { - eventBeat_->requestSynchronous(); +void EventQueue::experimental_flushSync(Tag tag) const { + eventBeat_->requestSynchronous(tag); } void EventQueue::onBeat(jsi::Runtime& runtime) const { diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h index 93fc8119e72d..d67875ac5fd1 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -63,7 +64,7 @@ class EventQueue { /* * Experimental API exposed to support EventEmitter::experimental_flushSync. */ - void experimental_flushSync() const; + void experimental_flushSync(Tag tag) const; protected: /* diff --git a/packages/react-native/ReactCommon/react/renderer/core/ReactPrimitives.h b/packages/react-native/ReactCommon/react/renderer/core/ReactPrimitives.h index bedbbad08c73..8df3a9c8b247 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ReactPrimitives.h +++ b/packages/react-native/ReactCommon/react/renderer/core/ReactPrimitives.h @@ -18,6 +18,11 @@ namespace facebook::react { */ using Tag = int32_t; +/* + * Value representing an unset tag. + */ +constexpr Tag kNoTag = -1; + /* * An id of a running Surface instance that is used to refer to the instance. */ diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp index 25318623afda..bd146530fb27 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.cpp @@ -14,7 +14,7 @@ namespace facebook::react { ShadowViewMutation ShadowViewMutation::CreateMutation(ShadowView shadowView) { return { /* .type = */ Create, - /* .parentTag = */ -1, + /* .parentTag = */ kNoTag, /* .oldChildShadowView = */ {}, /* .newChildShadowView = */ std::move(shadowView), /* .index = */ -1, @@ -24,7 +24,7 @@ ShadowViewMutation ShadowViewMutation::CreateMutation(ShadowView shadowView) { ShadowViewMutation ShadowViewMutation::DeleteMutation(ShadowView shadowView) { return { /* .type = */ Delete, - /* .parentTag = */ -1, + /* .parentTag = */ kNoTag, /* .oldChildShadowView = */ std::move(shadowView), /* .newChildShadowView = */ {}, /* .index = */ -1, @@ -131,7 +131,7 @@ std::vector getDebugProps( mutation.newChildShadowView, options)} : DebugStringConvertibleObject{}, - mutation.parentTag != -1 + mutation.parentTag != kNoTag ? DebugStringConvertibleObject{"parent", getDebugDescription( mutation.parentTag, diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.h index f74bf8551d08..7782a5a7ef2d 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowViewMutation.h @@ -94,7 +94,7 @@ struct ShadowViewMutation final { #pragma mark - Fields Type type = {Create}; - Tag parentTag = -1; + Tag parentTag = kNoTag; ShadowView oldChildShadowView = {}; ShadowView newChildShadowView = {}; int index = -1; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubView.h b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubView.h index 2d34fb112bd1..ffd166a0b0dc 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubView.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubView.h @@ -19,8 +19,6 @@ namespace facebook::react { -static const int NO_VIEW_TAG = -1; - class StubView final { public: using Shared = std::shared_ptr; @@ -42,7 +40,7 @@ class StubView final { LayoutMetrics layoutMetrics; State::Shared state; std::vector children; - Tag parentTag{NO_VIEW_TAG}; + Tag parentTag{kNoTag}; }; bool operator==(const StubView &lhs, const StubView &rhs); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubViewTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubViewTree.cpp index 24e19640bf39..53c2e40a242b 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubViewTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubViewTree.cpp @@ -51,7 +51,7 @@ void StubViewTree::mutate(const ShadowViewMutationList& mutations) { for (const auto& mutation : mutations) { switch (mutation.type) { case ShadowViewMutation::Create: { - react_native_assert(mutation.parentTag == -1); + react_native_assert(mutation.parentTag == kNoTag); react_native_assert(mutation.oldChildShadowView == ShadowView{}); react_native_assert(mutation.newChildShadowView.props); auto stubView = std::make_shared(); @@ -81,7 +81,7 @@ void StubViewTree::mutate(const ShadowViewMutationList& mutations) { << "] ##" << std::hash{}(mutation.oldChildShadowView); }); - react_native_assert(mutation.parentTag == -1); + react_native_assert(mutation.parentTag == kNoTag); react_native_assert(mutation.newChildShadowView == ShadowView{}); auto tag = mutation.oldChildShadowView.tag; react_native_assert(hasTag(tag)); @@ -118,7 +118,7 @@ void StubViewTree::mutate(const ShadowViewMutationList& mutations) { << parentTag << "] @" << mutation.index << "(" << parentStubView->children.size() << " children)"; }); - react_native_assert(childStubView->parentTag == NO_VIEW_TAG); + react_native_assert(childStubView->parentTag == kNoTag); react_native_assert( mutation.index >= 0 && parentStubView->children.size() >= @@ -182,7 +182,7 @@ void StubViewTree::mutate(const ShadowViewMutationList& mutations) { static_cast(mutation.index) && parentStubView->children[mutation.index]->tag == childStubView->tag); - childStubView->parentTag = NO_VIEW_TAG; + childStubView->parentTag = kNoTag; parentStubView->children.erase( parentStubView->children.begin() + mutation.index); } diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/EventBeatTest.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/EventBeatTest.cpp new file mode 100644 index 000000000000..40fe0cd0d7f4 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/EventBeatTest.cpp @@ -0,0 +1,188 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "StubQueue.h" + +namespace facebook::react { + +class EventBeatTestFeatureFlags : public ReactNativeFeatureFlagsDefaults { + public: + bool enableBridgelessArchitecture() override { + return true; + } +}; + +/* + * `induce` is protected: production code induces from platform beat + * subclasses. The tests drive it directly, standing in for the platform. + */ +class TestEventBeat : public EventBeat { + public: + using EventBeat::EventBeat; + using EventBeat::induce; +}; + +class EventBeatTest : public testing::Test { + protected: + void SetUp() override { + ReactNativeFeatureFlags::dangerouslyReset(); + ReactNativeFeatureFlags::override( + std::make_unique()); + + runtime_ = facebook::hermes::makeHermesRuntime( + ::hermes::vm::RuntimeConfig::Builder().build()); + stubQueue_ = std::make_unique(); + + RuntimeExecutor runtimeExecutor = + [this]( + std::function&& callback) { + stubQueue_->runOnQueue([this, callback = std::move(callback)]() { + callback(*runtime_); + }); + }; + + runtimeScheduler_ = std::make_unique(runtimeExecutor); + + ownerBox_ = std::make_shared(); + owner_ = std::make_shared(0); + ownerBox_->owner = owner_; + eventBeat_ = std::make_unique(ownerBox_, *runtimeScheduler_); + } + + void TearDown() override { + ReactNativeFeatureFlags::dangerouslyReset(); + } + + std::unique_ptr runtime_; + std::unique_ptr stubQueue_; + std::unique_ptr runtimeScheduler_; + std::shared_ptr ownerBox_; + std::shared_ptr owner_; + std::unique_ptr eventBeat_; +}; + +TEST_F(EventBeatTest, induceWithoutRequestIsNoop) { + int beatCount = 0; + eventBeat_->setBeatCallback([&beatCount](jsi::Runtime& /*runtime*/) { + beatCount++; + }); + + eventBeat_->induce(); + + EXPECT_EQ(beatCount, 0); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(EventBeatTest, synchronousRequestIsProcessedAtInduce) { + int beatCount = 0; + eventBeat_->setBeatCallback([&beatCount](jsi::Runtime& /*runtime*/) { + beatCount++; + }); + + eventBeat_->requestSynchronous(); + EXPECT_EQ(beatCount, 0); + + // Platform implementations induce the beat at a point where the effects of + // synchronous events can still make the current frame (the display phase on + // Apple, before the draw on Android). The beat callback runs synchronously + // before `induce` returns, with both threads blocked. + std::thread driver([this]() { + stubQueue_->waitForTask(); + stubQueue_->tick(); + }); + eventBeat_->induce(); + driver.join(); + + EXPECT_EQ(beatCount, 1); + + // The request was consumed: another induce does nothing. + eventBeat_->induce(); + EXPECT_EQ(beatCount, 1); + EXPECT_EQ(stubQueue_->size(), 0); +} + +TEST_F(EventBeatTest, requestMadeDuringBeatIsProcessedByASubsequentInduce) { + int beatCount = 0; + eventBeat_->setBeatCallback([&](jsi::Runtime& /*runtime*/) { + beatCount++; + if (beatCount == 1) { + // A synchronous request made from within the beat (e.g. an event whose + // handler causes another synchronous event). Platform implementations + // defer the induce for it (the display phase flusher on Apple, the next + // pre-draw on Android) rather than inducing from within the beat. + eventBeat_->requestSynchronous(); + } + }); + + eventBeat_->requestSynchronous(); + std::thread driver([this]() { + stubQueue_->waitForTask(); + stubQueue_->tick(); + }); + eventBeat_->induce(); + driver.join(); + + EXPECT_EQ(beatCount, 1); + + // The request made during the beat is not lost: the next induce processes + // it. + std::thread driver2([this]() { + stubQueue_->waitForTask(); + stubQueue_->tick(); + }); + eventBeat_->induce(); + driver2.join(); + + EXPECT_EQ(beatCount, 2); +} + +TEST_F(EventBeatTest, synchronousRequestIsNotStrandedBehindScheduledBeat) { + int beatCount = 0; + eventBeat_->setBeatCallback([&beatCount](jsi::Runtime& /*runtime*/) { + beatCount++; + }); + + // An asynchronous beat is scheduled but has not run yet. + eventBeat_->request(); + eventBeat_->induce(); + EXPECT_EQ(beatCount, 0); + + // A synchronous request arriving now must still be processed by its induce + // instead of being silently deferred behind the scheduled beat. + eventBeat_->requestSynchronous(); + std::thread inducer([this]() { eventBeat_->induce(); }); + + // Wait until the synchronous access request joins the already-queued work + // item, so that the tick order below is deterministic. + stubQueue_->waitForTasks(2); + + // The scheduled beat's work item yields to the pending synchronous access + // without executing. + stubQueue_->tick(); + EXPECT_EQ(beatCount, 0); + + // The synchronous access processes the beat within its induce. + stubQueue_->tick(); + inducer.join(); + EXPECT_EQ(beatCount, 1); + + // The beat that yielded resumes afterwards; it was not lost. + stubQueue_->tick(); + EXPECT_EQ(beatCount, 2); + EXPECT_EQ(stubQueue_->size(), 0); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp index 4dfd3a979e75..1e0626f3080b 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp @@ -389,8 +389,9 @@ void PointerEventsProcessor::processPendingPointerCapture( } auto pendingOverrideTag = - (hasPendingOverride) ? pendingOverride->getTag() : -1; - auto activeOverrideTag = (hasActiveOverride) ? activeOverride->getTag() : -1; + (hasPendingOverride) ? pendingOverride->getTag() : kNoTag; + auto activeOverrideTag = + (hasActiveOverride) ? activeOverride->getTag() : kNoTag; if (hasActiveOverride && activeOverrideTag != pendingOverrideTag) { auto retargeted = retargetPointerEvent(event, *activeOverride, uiManager); diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerViewTransitionDelegate.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerViewTransitionDelegate.h index 4c2482875ff4..53f796a808f3 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerViewTransitionDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerViewTransitionDelegate.h @@ -52,7 +52,7 @@ class UIManagerViewTransitionDelegate { Float y{0}; Float width{0}; Float height{0}; - Tag nativeTag{-1}; + Tag nativeTag{kNoTag}; }; virtual std::optional getViewTransitionInstance( diff --git a/packages/react-native/ReactCommon/react/renderer/viewtransition/ViewTransitionModule.cpp b/packages/react-native/ReactCommon/react/renderer/viewtransition/ViewTransitionModule.cpp index 57edf2878844..e550342b8373 100644 --- a/packages/react-native/ReactCommon/react/renderer/viewtransition/ViewTransitionModule.cpp +++ b/packages/react-native/ReactCommon/react/renderer/viewtransition/ViewTransitionModule.cpp @@ -554,7 +554,7 @@ ViewTransitionModule::getViewTransitionInstance( auto pseudoElementIt = oldPseudoElementNodes_.find(name); auto nativeTag = pseudoElementIt != oldPseudoElementNodes_.end() ? pseudoElementIt->second->getTag() - : -1; + : kNoTag; return ViewTransitionInstance{ .x = view.layoutMetrics.originFromRoot.x, .y = view.layoutMetrics.originFromRoot.y, diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 6ef9e8fdfd6d..834b58eb3769 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -482,6 +482,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -2235,8 +2236,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -2270,6 +2272,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 7a94e046a082..bc48b20f503e 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -482,6 +482,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -2218,8 +2219,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -2253,6 +2255,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 5881ae10d0f9..2441e79bda08 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -482,6 +482,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -2233,8 +2234,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -2268,6 +2270,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 4cb8f69152e0..bd2bdc122ad5 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -3407,6 +3407,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -4234,8 +4235,12 @@ class facebook::react::AppRegistryBinding { } class facebook::react::AppleEventBeat : public facebook::react::EventBeat, public facebook::react::RunLoopObserver::Delegate { - public AppleEventBeat(std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, facebook::react::RuntimeScheduler& RuntimeScheduler); + public AppleEventBeat(std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, facebook::react::RuntimeScheduler& RuntimeScheduler, facebook::react::AppleEventBeat::WindowLayerResolver windowLayerResolver); + public using WindowLayerResolver = std::function; public virtual void activityDidChange(const facebook::react::RunLoopObserver::Delegate* delegate, facebook::react::RunLoopObserver::Activity activity) const noexcept override; + public virtual void requestSynchronous(facebook::react::Tag tag) const override; + public void requestSynchronous() const; + public ~AppleEventBeat() override; } class facebook::react::AsyncArrayBuffer { @@ -4748,8 +4753,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -4771,6 +4777,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index efcf207217fd..4099a2828d71 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -3399,6 +3399,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -4221,8 +4222,12 @@ class facebook::react::AppRegistryBinding { } class facebook::react::AppleEventBeat : public facebook::react::EventBeat, public facebook::react::RunLoopObserver::Delegate { - public AppleEventBeat(std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, facebook::react::RuntimeScheduler& RuntimeScheduler); + public AppleEventBeat(std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, facebook::react::RuntimeScheduler& RuntimeScheduler, facebook::react::AppleEventBeat::WindowLayerResolver windowLayerResolver); + public using WindowLayerResolver = std::function; public virtual void activityDidChange(const facebook::react::RunLoopObserver::Delegate* delegate, facebook::react::RunLoopObserver::Activity activity) const noexcept override; + public virtual void requestSynchronous(facebook::react::Tag tag) const override; + public void requestSynchronous() const; + public ~AppleEventBeat() override; } class facebook::react::AsyncArrayBuffer { @@ -4724,8 +4729,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -4747,6 +4753,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index fe198bfd356b..7e2828712970 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -3407,6 +3407,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -4232,8 +4233,12 @@ class facebook::react::AppRegistryBinding { } class facebook::react::AppleEventBeat : public facebook::react::EventBeat, public facebook::react::RunLoopObserver::Delegate { - public AppleEventBeat(std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, facebook::react::RuntimeScheduler& RuntimeScheduler); + public AppleEventBeat(std::shared_ptr ownerBox, std::unique_ptr uiRunLoopObserver, facebook::react::RuntimeScheduler& RuntimeScheduler, facebook::react::AppleEventBeat::WindowLayerResolver windowLayerResolver); + public using WindowLayerResolver = std::function; public virtual void activityDidChange(const facebook::react::RunLoopObserver::Delegate* delegate, facebook::react::RunLoopObserver::Activity activity) const noexcept override; + public virtual void requestSynchronous(facebook::react::Tag tag) const override; + public void requestSynchronous() const; + public ~AppleEventBeat() override; } class facebook::react::AsyncArrayBuffer { @@ -4746,8 +4751,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -4769,6 +4775,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index e6e84e48c02a..6e19a9c0ca16 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -178,6 +178,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -1476,8 +1477,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -1499,6 +1501,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index ca7b7c2e26b2..b2be2b5c0eff 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -178,6 +178,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -1460,8 +1461,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -1483,6 +1485,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index acb87a6fd70f..89bb5d946036 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -178,6 +178,7 @@ constexpr char* const facebook::react::TextLayoutManagerKey; constexpr facebook::react::HighResDuration facebook::react::DEFAULT_DURATION_THRESHOLD; constexpr facebook::react::HighResDuration facebook::react::LONG_TASK_DURATION_THRESHOLD; constexpr facebook::react::ReactNativeVersionType facebook::react::ReactNativeVersion; +constexpr facebook::react::Tag facebook::react::kNoTag; constexpr float facebook::react::kDefaultEpsilon; constexpr size_t facebook::react::EVENT_BUFFER_SIZE; constexpr size_t facebook::react::LONG_TASK_BUFFER_SIZE; @@ -1474,8 +1475,9 @@ class facebook::react::EventBeat { public using BeatCallback = std::function; public using Factory = std::function(std::shared_ptr ownerBox)>; public virtual void request() const; - public virtual void requestSynchronous() const; + public virtual void requestSynchronous(facebook::react::Tag tag) const; public virtual ~EventBeat() = default; + public void requestSynchronous() const; public void setBeatCallback(facebook::react::EventBeat::BeatCallback beatCallback); } @@ -1497,6 +1499,7 @@ class facebook::react::EventDispatcher { class facebook::react::EventEmitter { public EventEmitter(facebook::react::SharedEventTarget eventTarget, facebook::react::EventDispatcher::Weak eventDispatcher); public const facebook::react::SharedEventTarget& getEventTarget() const; + public facebook::react::Tag getTag() const; public static facebook::react::ValueFactory defaultPayloadFactory(); public static std::mutex& DispatchMutex(); public static std::string normalizeEventType(std::string type);