forked from microsoft/react-native-macos
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNativeMutationObserver.cpp
More file actions
164 lines (137 loc) · 5.37 KB
/
NativeMutationObserver.cpp
File metadata and controls
164 lines (137 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
* 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 "NativeMutationObserver.h"
#include <cxxreact/TraceSection.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/primitives.h>
#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
#include "Plugins.h"
#endif
std::shared_ptr<facebook::react::TurboModule>
NativeMutationObserverModuleProvider(
std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
return std::make_shared<facebook::react::NativeMutationObserver>(
std::move(jsInvoker));
}
namespace facebook::react {
static UIManager& getUIManagerFromRuntime(jsi::Runtime& runtime) {
return UIManagerBinding::getBinding(runtime)->getUIManager();
}
NativeMutationObserver::NativeMutationObserver(
std::shared_ptr<CallInvoker> jsInvoker)
: NativeMutationObserverCxxSpec(std::move(jsInvoker)) {}
void NativeMutationObserver::observe(
jsi::Runtime& runtime,
const NativeMutationObserverObserveOptions& options) {
auto mutationObserverId = options.mutationObserverId;
auto subtree = options.subtree;
auto shadowNode = options.targetShadowNode;
auto& uiManager = getUIManagerFromRuntime(runtime);
mutationObserverManager_.observe(
mutationObserverId, shadowNode, subtree, uiManager);
}
void NativeMutationObserver::unobserveAll(
jsi::Runtime& /*runtime*/,
MutationObserverId mutationObserverId) {
mutationObserverManager_.unobserveAll(mutationObserverId);
}
void NativeMutationObserver::connect(
jsi::Runtime& runtime,
jsi::Function notifyMutationObservers,
SyncCallback<jsi::Value(jsi::Value)> getPublicInstanceFromInstanceHandle) {
auto& uiManager = getUIManagerFromRuntime(runtime);
runtime_ = &runtime;
notifyMutationObservers_.emplace(std::move(notifyMutationObservers));
getPublicInstanceFromInstanceHandle_.emplace(
std::move(getPublicInstanceFromInstanceHandle));
auto onMutationsCallback = [&](std::vector<MutationRecord>& records) {
return onMutations(records);
};
mutationObserverManager_.connect(uiManager, std::move(onMutationsCallback));
}
void NativeMutationObserver::disconnect(jsi::Runtime& runtime) {
auto& uiManager = getUIManagerFromRuntime(runtime);
mutationObserverManager_.disconnect(uiManager);
runtime_ = nullptr;
notifyMutationObservers_.reset();
getPublicInstanceFromInstanceHandle_.reset();
}
std::vector<NativeMutationRecord> NativeMutationObserver::takeRecords(
jsi::Runtime& /*runtime*/) {
notifiedMutationObservers_ = false;
std::vector<NativeMutationRecord> records;
pendingRecords_.swap(records);
return records;
}
jsi::Value NativeMutationObserver::getPublicInstanceFromShadowNode(
const ShadowNode& shadowNode) const {
auto instanceHandle = shadowNode.getInstanceHandle(*runtime_);
if (!instanceHandle.isObject()) {
return jsi::Value::null();
}
return getPublicInstanceFromInstanceHandle_.value().call(
std::move(instanceHandle));
}
std::vector<jsi::Value>
NativeMutationObserver::getPublicInstancesFromShadowNodes(
const std::vector<std::shared_ptr<const ShadowNode>>& shadowNodes) const {
std::vector<jsi::Value> publicInstances;
publicInstances.reserve(shadowNodes.size());
for (const auto& shadowNode : shadowNodes) {
publicInstances.push_back(getPublicInstanceFromShadowNode(*shadowNode));
}
return publicInstances;
}
void NativeMutationObserver::onMutations(std::vector<MutationRecord>& records) {
TraceSection s("NativeMutationObserver::onMutations");
for (const auto& record : records) {
pendingRecords_.emplace_back(
NativeMutationRecord{
record.mutationObserverId,
// It's safe to call into JS here because we only check mutations
// synchronously when committing from React (so we're in a JS
// task already).
getPublicInstanceFromShadowNode(*record.targetShadowNode),
getPublicInstancesFromShadowNodes(record.addedShadowNodes),
getPublicInstancesFromShadowNodes(record.removedShadowNodes)});
}
notifyMutationObserversIfNecessary();
}
/**
* This method allows us to avoid scheduling multiple calls to notify observers
* in the JS thread. We schedule one and skip subsequent ones (we just append
* the records to the pending list and wait for the scheduled task to consume
* all of them).
*/
void NativeMutationObserver::notifyMutationObserversIfNecessary() {
bool dispatchNotification = false;
if (!pendingRecords_.empty() && !notifiedMutationObservers_) {
notifiedMutationObservers_ = true;
dispatchNotification = true;
}
if (dispatchNotification) {
TraceSection s("NativeMutationObserver::notifyObservers");
#ifndef RCT_REMOVE_LEGACY_ARCH
if (ReactNativeFeatureFlags::enableBridgelessArchitecture()) {
#endif
runtime_->queueMicrotask(notifyMutationObservers_.value());
#ifndef RCT_REMOVE_LEGACY_ARCH
} else {
jsInvoker_->invokeAsync([&](jsi::Runtime& runtime) {
// It's possible that the last observer was disconnected before we could
// dispatch this notification.
if (notifyMutationObservers_) {
notifyMutationObservers_.value().call(runtime);
}
});
}
#endif
}
}
} // namespace facebook::react