/* * 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. */ #import "RCTMountingManager.h" #import #import #import #import #import #import #import #import "RCTComponentViewProtocol.h" #import "RCTComponentViewRegistry.h" #import "RCTConversions.h" #import "RCTMountingTransactionObserverCoordinator.h" using namespace facebook; using namespace facebook::react; // `Create` instruction static void RNCreateMountInstruction( ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry, RCTMountingTransactionObserverCoordinator &observerCoordinator, SurfaceId surfaceId) { auto componentViewDescriptor = [registry dequeueComponentViewWithComponentHandle:mutation.newChildShadowView.componentHandle tag:mutation.newChildShadowView.tag]; observerCoordinator.registerViewComponentDescriptor(componentViewDescriptor, surfaceId); } // `Delete` instruction static void RNDeleteMountInstruction( ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry, RCTMountingTransactionObserverCoordinator &observerCoordinator, SurfaceId surfaceId) { auto const &oldChildShadowView = mutation.oldChildShadowView; auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:oldChildShadowView.tag]; observerCoordinator.unregisterViewComponentDescriptor(componentViewDescriptor, surfaceId); [registry enqueueComponentViewWithComponentHandle:oldChildShadowView.componentHandle tag:oldChildShadowView.tag componentViewDescriptor:componentViewDescriptor]; } // `Insert` instruction static void RNInsertMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &newShadowView = mutation.newChildShadowView; auto const &parentShadowView = mutation.parentShadowView; auto const &childComponentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; auto const &parentComponentViewDescriptor = [registry componentViewDescriptorWithTag:parentShadowView.tag]; [parentComponentViewDescriptor.view mountChildComponentView:childComponentViewDescriptor.view index:mutation.index]; } // `Remove` instruction static void RNRemoveMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &oldShadowView = mutation.oldChildShadowView; auto const &parentShadowView = mutation.parentShadowView; auto const &childComponentViewDescriptor = [registry componentViewDescriptorWithTag:oldShadowView.tag]; auto const &parentComponentViewDescriptor = [registry componentViewDescriptorWithTag:parentShadowView.tag]; [parentComponentViewDescriptor.view unmountChildComponentView:childComponentViewDescriptor.view index:mutation.index]; } // `Update Props` instruction static void RNUpdatePropsMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; [componentViewDescriptor.view updateProps:newShadowView.props oldProps:oldShadowView.props]; } // `Update EventEmitter` instruction static void RNUpdateEventEmitterMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &newShadowView = mutation.newChildShadowView; auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; [componentViewDescriptor.view updateEventEmitter:newShadowView.eventEmitter]; } // `Update LayoutMetrics` instruction static void RNUpdateLayoutMetricsMountInstruction( ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; [componentViewDescriptor.view updateLayoutMetrics:newShadowView.layoutMetrics oldLayoutMetrics:oldShadowView.layoutMetrics]; } // `Update State` instruction static void RNUpdateStateMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; [componentViewDescriptor.view updateState:newShadowView.state oldState:oldShadowView.state]; } // `Finalize Updates` instruction static void RNFinalizeUpdatesMountInstruction( ShadowViewMutation const &mutation, RNComponentViewUpdateMask mask, RCTComponentViewRegistry *registry) { auto const &newShadowView = mutation.newChildShadowView; auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; [componentViewDescriptor.view finalizeUpdates:mask]; } // `Update` instruction static void RNPerformMountInstructions( ShadowViewMutationList const &mutations, RCTComponentViewRegistry *registry, RCTMountingTransactionObserverCoordinator &observerCoordinator, SurfaceId surfaceId) { SystraceSection s("RNPerformMountInstructions"); [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; for (auto const &mutation : mutations) { switch (mutation.type) { case ShadowViewMutation::Create: { RNCreateMountInstruction(mutation, registry, observerCoordinator, surfaceId); break; } case ShadowViewMutation::Delete: { RNDeleteMountInstruction(mutation, registry, observerCoordinator, surfaceId); break; } case ShadowViewMutation::Insert: { RNUpdatePropsMountInstruction(mutation, registry); RNUpdateEventEmitterMountInstruction(mutation, registry); RNUpdateStateMountInstruction(mutation, registry); RNUpdateLayoutMetricsMountInstruction(mutation, registry); RNFinalizeUpdatesMountInstruction(mutation, RNComponentViewUpdateMaskAll, registry); RNInsertMountInstruction(mutation, registry); break; } case ShadowViewMutation::Remove: { RNRemoveMountInstruction(mutation, registry); break; } case ShadowViewMutation::Update: { auto const &oldChildShadowView = mutation.oldChildShadowView; auto const &newChildShadowView = mutation.newChildShadowView; auto mask = RNComponentViewUpdateMask{}; if (oldChildShadowView.props != newChildShadowView.props) { RNUpdatePropsMountInstruction(mutation, registry); mask |= RNComponentViewUpdateMaskProps; } if (oldChildShadowView.eventEmitter != newChildShadowView.eventEmitter) { RNUpdateEventEmitterMountInstruction(mutation, registry); mask |= RNComponentViewUpdateMaskEventEmitter; } if (oldChildShadowView.state != newChildShadowView.state) { RNUpdateStateMountInstruction(mutation, registry); mask |= RNComponentViewUpdateMaskState; } if (oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) { RNUpdateLayoutMetricsMountInstruction(mutation, registry); mask |= RNComponentViewUpdateMaskLayoutMetrics; } if (mask != RNComponentViewUpdateMaskNone) { RNFinalizeUpdatesMountInstruction(mutation, mask, registry); } break; } } } [CATransaction commit]; } @implementation RCTMountingManager { RCTMountingTransactionObserverCoordinator _observerCoordinator; BOOL _transactionInFlight; BOOL _followUpTransactionRequired; } - (instancetype)init { if (self = [super init]) { _componentViewRegistry = [[RCTComponentViewRegistry alloc] init]; } return self; } - (void)scheduleTransaction:(MountingCoordinator::Shared const &)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; } auto mountingCoordinatorCopy = mountingCoordinator; RCTExecuteOnMainQueue(^{ RCTAssertMainQueue(); [self initiateTransaction:mountingCoordinatorCopy]; }); } - (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(^{ RCTAssertMainQueue(); [self synchronouslyDispatchCommandOnUIThread:reactTag commandName:commandName args:args]; }); } - (void)initiateTransaction:(MountingCoordinator::Shared const &)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:(MountingCoordinator::Shared const &)mountingCoordinator { SystraceSection s("-[RCTMountingManager performTransaction:]"); RCTAssertMainQueue(); auto differentiatorMode = self.useModernDifferentiatorMode ? DifferentiatorMode::OptimizedMoves : DifferentiatorMode::Classic; auto transaction = mountingCoordinator->pullTransaction(differentiatorMode); if (!transaction.has_value()) { return; } auto surfaceId = transaction->getSurfaceId(); auto &mutations = transaction->getMutations(); if (mutations.size() == 0) { return; } auto telemetry = transaction->getTelemetry(); auto number = transaction->getNumber(); [self.delegate mountingManager:self willMountComponentsWithRootTag:surfaceId]; _observerCoordinator.notifyObserversMountingTransactionWillMount({surfaceId, number, telemetry}); telemetry.willMount(); RNPerformMountInstructions(mutations, self.componentViewRegistry, _observerCoordinator, surfaceId); telemetry.didMount(); _observerCoordinator.notifyObserversMountingTransactionDidMount({surfaceId, number, telemetry}); [self.delegate mountingManager:self didMountComponentsWithRootTag:surfaceId]; } - (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag changedProps:(NSDictionary *)props componentDescriptor:(const ComponentDescriptor &)componentDescriptor { RCTAssertMainQueue(); UIView *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag]; SharedProps oldProps = [componentView props]; SharedProps newProps = componentDescriptor.cloneProps(oldProps, RawProps(convertIdToFollyDynamic(props))); [componentView updateProps:newProps oldProps:oldProps]; } - (void)synchronouslyDispatchCommandOnUIThread:(ReactTag)reactTag commandName:(NSString *)commandName args:(NSArray *)args { RCTAssertMainQueue(); UIView *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag]; [componentView handleCommand:commandName args:args]; } @end