Remove pending records when unobserving a target in IntersectionObserver (#41450)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/41450

`IntersectionObserver` shouldn't report entries for targets that are no longer being observed by the observer. This wasn't the case before because it was possible to create an intersection observer entry, then unobserve the target and then dispatch the pending entries (including the unobserved target). This fixes that issue to align with Web browsers.

Changelog: [internal]

Reviewed By: rshest

Differential Revision: D51256827

fbshipit-source-id: 28035f00bcb05a8ca53140719019032b3399436c
This commit is contained in:
Rubén Norte
2023-11-14 06:11:13 -08:00
committed by Facebook GitHub Bot
parent fc0c084df0
commit a493ea43ff
2 changed files with 46 additions and 20 deletions
@@ -96,6 +96,16 @@ const NativeIntersectionObserverMock = {
'unexpected duplicate call to unobserve',
);
observations.splice(observationIndex, 1);
pendingRecords = pendingRecords.filter(
record =>
record.intersectionObserverId !== intersectionObserverId ||
record.targetInstanceHandle !==
FabricUIManagerMock.__getInstanceHandleFromNode(
// $FlowExpectedError[incompatible-call]
targetShadowNode,
),
);
},
connect: (notifyIntersectionObserversCallback: () => void): void => {
invariant(callback == null, 'unexpected call to connect');
@@ -70,31 +70,47 @@ void IntersectionObserverManager::unobserve(
const ShadowNode& shadowNode) {
SystraceSection s("IntersectionObserverManager::unobserve");
std::unique_lock lock(observersMutex_);
{
std::unique_lock lock(observersMutex_);
auto surfaceId = shadowNode.getSurfaceId();
auto surfaceId = shadowNode.getSurfaceId();
auto observersIt = observersBySurfaceId_.find(surfaceId);
if (observersIt == observersBySurfaceId_.end()) {
return;
auto observersIt = observersBySurfaceId_.find(surfaceId);
if (observersIt == observersBySurfaceId_.end()) {
return;
}
auto& observers = observersIt->second;
observers.erase(
std::remove_if(
observers.begin(),
observers.end(),
[intersectionObserverId, &shadowNode](const auto& observer) {
return observer.getIntersectionObserverId() ==
intersectionObserverId &&
ShadowNode::sameFamily(
observer.getTargetShadowNode(), shadowNode);
}),
observers.end());
if (observers.empty()) {
observersBySurfaceId_.erase(surfaceId);
}
}
auto& observers = observersIt->second;
{
std::unique_lock lock(pendingEntriesMutex_);
observers.erase(
std::remove_if(
observers.begin(),
observers.end(),
[intersectionObserverId, &shadowNode](const auto& observer) {
return observer.getIntersectionObserverId() ==
intersectionObserverId &&
ShadowNode::sameFamily(
observer.getTargetShadowNode(), shadowNode);
}),
observers.end());
if (observers.empty()) {
observersBySurfaceId_.erase(surfaceId);
pendingEntries_.erase(
std::remove_if(
pendingEntries_.begin(),
pendingEntries_.end(),
[intersectionObserverId, &shadowNode](const auto& entry) {
return entry.intersectionObserverId == intersectionObserverId &&
ShadowNode::sameFamily(*entry.shadowNode, shadowNode);
}),
pendingEntries_.end());
}
}