-
Notifications
You must be signed in to change notification settings - Fork 24.4k
/
RCTMountingManager.mm
340 lines (282 loc) · 13 KB
/
RCTMountingManager.mm
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
* 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.
*/
#import "RCTMountingManager.h"
#import <QuartzCore/QuartzCore.h>
#import <React/RCTAssert.h>
#import <React/RCTComponent.h>
#import <React/RCTFollyConvert.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
#import <cxxreact/SystraceSection.h>
#import <react/config/ReactNativeConfig.h>
#import <react/renderer/components/root/RootShadowNode.h>
#import <react/renderer/core/LayoutableShadowNode.h>
#import <react/renderer/core/RawProps.h>
#import <react/renderer/mounting/TelemetryController.h>
#import <react/utils/CoreFeatures.h>
#import <React/RCTComponentViewProtocol.h>
#import <React/RCTComponentViewRegistry.h>
#import <React/RCTConversions.h>
#import <React/RCTMountingTransactionObserverCoordinator.h>
using namespace facebook::react;
static SurfaceId RCTSurfaceIdForView(UIView *view)
{
do {
if (RCTIsReactRootView(@(view.tag))) {
return view.tag;
}
view = view.superview;
} while (view != nil);
return -1;
}
static void RCTPerformMountInstructions(
const ShadowViewMutationList &mutations,
RCTComponentViewRegistry *registry,
RCTMountingTransactionObserverCoordinator &observerCoordinator,
SurfaceId surfaceId)
{
SystraceSection s("RCTPerformMountInstructions");
for (const auto &mutation : mutations) {
switch (mutation.type) {
case ShadowViewMutation::Create: {
auto &newChildShadowView = mutation.newChildShadowView;
auto &newChildViewDescriptor =
[registry dequeueComponentViewWithComponentHandle:newChildShadowView.componentHandle
tag:newChildShadowView.tag];
observerCoordinator.registerViewComponentDescriptor(newChildViewDescriptor, surfaceId);
break;
}
case ShadowViewMutation::Delete: {
auto &oldChildShadowView = mutation.oldChildShadowView;
auto &oldChildViewDescriptor = [registry componentViewDescriptorWithTag:oldChildShadowView.tag];
observerCoordinator.unregisterViewComponentDescriptor(oldChildViewDescriptor, surfaceId);
[registry enqueueComponentViewWithComponentHandle:oldChildShadowView.componentHandle
tag:oldChildShadowView.tag
componentViewDescriptor:oldChildViewDescriptor];
break;
}
case ShadowViewMutation::Insert: {
auto &newChildShadowView = mutation.newChildShadowView;
auto &parentShadowView = mutation.parentShadowView;
auto &newChildViewDescriptor = [registry componentViewDescriptorWithTag:newChildShadowView.tag];
auto &parentViewDescriptor = [registry componentViewDescriptorWithTag:parentShadowView.tag];
UIView<RCTComponentViewProtocol> *newChildComponentView = newChildViewDescriptor.view;
RCTAssert(newChildShadowView.props, @"`newChildShadowView.props` must not be null.");
[newChildComponentView updateProps:newChildShadowView.props oldProps:nullptr];
[newChildComponentView updateEventEmitter:newChildShadowView.eventEmitter];
[newChildComponentView updateState:newChildShadowView.state oldState:nullptr];
[newChildComponentView updateLayoutMetrics:newChildShadowView.layoutMetrics
oldLayoutMetrics:EmptyLayoutMetrics];
[newChildComponentView finalizeUpdates:RNComponentViewUpdateMaskAll];
[parentViewDescriptor.view mountChildComponentView:newChildComponentView index:mutation.index];
break;
}
case ShadowViewMutation::Remove: {
auto &oldChildShadowView = mutation.oldChildShadowView;
auto &parentShadowView = mutation.parentShadowView;
auto &oldChildViewDescriptor = [registry componentViewDescriptorWithTag:oldChildShadowView.tag];
auto &parentViewDescriptor = [registry componentViewDescriptorWithTag:parentShadowView.tag];
[parentViewDescriptor.view unmountChildComponentView:oldChildViewDescriptor.view index:mutation.index];
break;
}
case ShadowViewMutation::Update: {
auto &oldChildShadowView = mutation.oldChildShadowView;
auto &newChildShadowView = mutation.newChildShadowView;
auto &newChildViewDescriptor = [registry componentViewDescriptorWithTag:newChildShadowView.tag];
UIView<RCTComponentViewProtocol> *newChildComponentView = newChildViewDescriptor.view;
auto mask = RNComponentViewUpdateMask{};
RCTAssert(newChildShadowView.props, @"`newChildShadowView.props` must not be null.");
if (oldChildShadowView.props != newChildShadowView.props) {
[newChildComponentView updateProps:newChildShadowView.props oldProps:oldChildShadowView.props];
mask |= RNComponentViewUpdateMaskProps;
}
if (oldChildShadowView.eventEmitter != newChildShadowView.eventEmitter) {
[newChildComponentView updateEventEmitter:newChildShadowView.eventEmitter];
mask |= RNComponentViewUpdateMaskEventEmitter;
}
if (oldChildShadowView.state != newChildShadowView.state) {
[newChildComponentView updateState:newChildShadowView.state oldState:oldChildShadowView.state];
mask |= RNComponentViewUpdateMaskState;
}
if (oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) {
[newChildComponentView updateLayoutMetrics:newChildShadowView.layoutMetrics
oldLayoutMetrics:oldChildShadowView.layoutMetrics];
mask |= RNComponentViewUpdateMaskLayoutMetrics;
}
if (mask != RNComponentViewUpdateMaskNone) {
[newChildComponentView finalizeUpdates:mask];
}
break;
}
}
}
}
@implementation RCTMountingManager {
RCTMountingTransactionObserverCoordinator _observerCoordinator;
BOOL _transactionInFlight;
BOOL _followUpTransactionRequired;
ContextContainer::Shared _contextContainer;
}
- (instancetype)init
{
if (self = [super init]) {
_componentViewRegistry = [RCTComponentViewRegistry new];
}
return self;
}
- (void)setContextContainer:(ContextContainer::Shared)contextContainer
{
_contextContainer = contextContainer;
}
- (void)attachSurfaceToView:(UIView *)view surfaceId:(SurfaceId)surfaceId
{
RCTAssertMainQueue();
RCTAssert(view.subviews.count == 0, @"The view must not have any subviews.");
RCTComponentViewDescriptor rootViewDescriptor =
[_componentViewRegistry dequeueComponentViewWithComponentHandle:RootShadowNode::Handle() tag:surfaceId];
[view addSubview:rootViewDescriptor.view];
}
- (void)detachSurfaceFromView:(UIView *)view surfaceId:(SurfaceId)surfaceId
{
RCTAssertMainQueue();
RCTComponentViewDescriptor rootViewDescriptor = [_componentViewRegistry componentViewDescriptorWithTag:surfaceId];
[rootViewDescriptor.view removeFromSuperview];
[_componentViewRegistry enqueueComponentViewWithComponentHandle:RootShadowNode::Handle()
tag:surfaceId
componentViewDescriptor:rootViewDescriptor];
}
- (void)scheduleTransaction:(MountingCoordinator::Shared)mountingCoordinator
{
if (RCTIsMainQueue()) {
// Already on the proper thread, so:
// * No need to do a thread jump;
// * No need to do expensive copy of all mutations;
// * No need to allocate a block.
[self initiateTransaction:*mountingCoordinator];
return;
}
RCTExecuteOnMainQueue(^{
RCTAssertMainQueue();
[self initiateTransaction:*mountingCoordinator];
});
}
- (void)dispatchCommand:(ReactTag)reactTag commandName:(NSString *)commandName args:(NSArray *)args
{
if (RCTIsMainQueue()) {
// Already on the proper thread, so:
// * No need to do a thread jump;
// * No need to allocate a block.
[self synchronouslyDispatchCommandOnUIThread:reactTag commandName:commandName args:args];
return;
}
RCTExecuteOnMainQueue(^{
[self synchronouslyDispatchCommandOnUIThread:reactTag commandName:commandName args:args];
});
}
- (void)sendAccessibilityEvent:(ReactTag)reactTag eventType:(NSString *)eventType
{
if (RCTIsMainQueue()) {
// Already on the proper thread, so:
// * No need to do a thread jump;
// * No need to allocate a block.
[self synchronouslyDispatchAccessbilityEventOnUIThread:reactTag eventType:eventType];
return;
}
RCTExecuteOnMainQueue(^{
[self synchronouslyDispatchAccessbilityEventOnUIThread:reactTag eventType:eventType];
});
}
- (void)initiateTransaction:(const MountingCoordinator &)mountingCoordinator
{
SystraceSection s("-[RCTMountingManager initiateTransaction:]");
RCTAssertMainQueue();
if (_transactionInFlight) {
_followUpTransactionRequired = YES;
return;
}
do {
_followUpTransactionRequired = NO;
_transactionInFlight = YES;
[self performTransaction:mountingCoordinator];
_transactionInFlight = NO;
} while (_followUpTransactionRequired);
}
- (void)performTransaction:(const MountingCoordinator &)mountingCoordinator
{
SystraceSection s("-[RCTMountingManager performTransaction:]");
RCTAssertMainQueue();
auto surfaceId = mountingCoordinator.getSurfaceId();
mountingCoordinator.getTelemetryController().pullTransaction(
[&](const MountingTransaction &transaction, const SurfaceTelemetry &surfaceTelemetry) {
[self.delegate mountingManager:self willMountComponentsWithRootTag:surfaceId];
_observerCoordinator.notifyObserversMountingTransactionWillMount(transaction, surfaceTelemetry);
},
[&](const MountingTransaction &transaction, const SurfaceTelemetry &surfaceTelemetry) {
RCTPerformMountInstructions(
transaction.getMutations(), _componentViewRegistry, _observerCoordinator, surfaceId);
},
[&](const MountingTransaction &transaction, const SurfaceTelemetry &surfaceTelemetry) {
_observerCoordinator.notifyObserversMountingTransactionDidMount(transaction, surfaceTelemetry);
[self.delegate mountingManager:self didMountComponentsWithRootTag:surfaceId];
});
}
- (void)setIsJSResponder:(BOOL)isJSResponder
blockNativeResponder:(BOOL)blockNativeResponder
forShadowView:(const facebook::react::ShadowView &)shadowView
{
ReactTag reactTag = shadowView.tag;
RCTExecuteOnMainQueue(^{
UIView<RCTComponentViewProtocol> *componentView = [self->_componentViewRegistry findComponentViewWithTag:reactTag];
[componentView setIsJSResponder:isJSResponder];
});
}
- (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag
changedProps:(NSDictionary *)props
componentDescriptor:(const ComponentDescriptor &)componentDescriptor
{
RCTAssertMainQueue();
UIView<RCTComponentViewProtocol> *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag];
SurfaceId surfaceId = RCTSurfaceIdForView(componentView);
Props::Shared oldProps = [componentView props];
Props::Shared newProps = componentDescriptor.cloneProps(
PropsParserContext{surfaceId, *_contextContainer.get()}, oldProps, RawProps(convertIdToFollyDynamic(props)));
NSSet<NSString *> *propKeys = componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN ?: [NSSet new];
propKeys = [propKeys setByAddingObjectsFromArray:props.allKeys];
componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = nil;
[componentView updateProps:newProps oldProps:oldProps];
componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = propKeys;
const auto &newViewProps = static_cast<const ViewProps &>(*newProps);
if (props[@"transform"]) {
auto layoutMetrics = LayoutMetrics();
layoutMetrics.frame.size.width = componentView.layer.bounds.size.width;
layoutMetrics.frame.size.height = componentView.layer.bounds.size.height;
CATransform3D newTransform = RCTCATransform3DFromTransformMatrix(newViewProps.resolveTransform(layoutMetrics));
if (!CATransform3DEqualToTransform(newTransform, componentView.layer.transform)) {
componentView.layer.transform = newTransform;
}
}
if (props[@"opacity"] && componentView.layer.opacity != (float)newViewProps.opacity) {
componentView.layer.opacity = newViewProps.opacity;
}
[componentView finalizeUpdates:RNComponentViewUpdateMaskProps];
}
- (void)synchronouslyDispatchCommandOnUIThread:(ReactTag)reactTag
commandName:(NSString *)commandName
args:(NSArray *)args
{
RCTAssertMainQueue();
UIView<RCTComponentViewProtocol> *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag];
[componentView handleCommand:commandName args:args];
}
- (void)synchronouslyDispatchAccessbilityEventOnUIThread:(ReactTag)reactTag eventType:(NSString *)eventType
{
if ([@"focus" isEqualToString:eventType]) {
UIView<RCTComponentViewProtocol> *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag];
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, componentView);
}
}
@end