/* * 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 "RCTSurfacePresenter.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "PlatformRunLoopObserver.h" #import "RCTConversions.h" using namespace facebook::react; static dispatch_queue_t RCTGetBackgroundQueue() { static dispatch_queue_t queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0); queue = dispatch_queue_create("com.facebook.react.background", attr); }); return queue; } static BackgroundExecutor RCTGetBackgroundExecutor() { return [](std::function &&callback) { if (RCTIsMainQueue()) { callback(); return; } auto copyableCallback = callback; dispatch_async(RCTGetBackgroundQueue(), ^{ copyableCallback(); }); }; } @interface RCTSurfacePresenter () @end @implementation RCTSurfacePresenter { RCTMountingManager *_mountingManager; // Thread-safe. RCTSurfaceRegistry *_surfaceRegistry; // Thread-safe. std::mutex _schedulerAccessMutex; std::mutex _schedulerLifeCycleMutex; RCTScheduler *_Nullable _scheduler; // Thread-safe. Pointer is protected by `_schedulerAccessMutex`. ContextContainer::Shared _contextContainer; // Protected by `_schedulerLifeCycleMutex`. RuntimeExecutor _runtimeExecutor; // Protected by `_schedulerLifeCycleMutex`. better::shared_mutex _observerListMutex; NSMutableArray> *_observers; } - (instancetype)initWithContextContainer:(ContextContainer::Shared)contextContainer runtimeExecutor:(RuntimeExecutor)runtimeExecutor { if (self = [super init]) { assert(contextContainer && "RuntimeExecutor must be not null."); _runtimeExecutor = runtimeExecutor; _contextContainer = contextContainer; _surfaceRegistry = [[RCTSurfaceRegistry alloc] init]; _mountingManager = [[RCTMountingManager alloc] init]; _mountingManager.delegate = self; _observers = [NSMutableArray array]; _scheduler = [self _createScheduler]; } return self; } - (RCTMountingManager *)mountingManager { return _mountingManager; } - (RCTScheduler *_Nullable)_scheduler { std::lock_guard lock(_schedulerAccessMutex); return _scheduler; } - (ContextContainer::Shared)contextContainer { std::lock_guard lock(_schedulerLifeCycleMutex); return _contextContainer; } - (void)setContextContainer:(ContextContainer::Shared)contextContainer { std::lock_guard lock(_schedulerLifeCycleMutex); _contextContainer = contextContainer; } - (RuntimeExecutor)runtimeExecutor { std::lock_guard lock(_schedulerLifeCycleMutex); return _runtimeExecutor; } - (void)setRuntimeExecutor:(RuntimeExecutor)runtimeExecutor { std::lock_guard lock(_schedulerLifeCycleMutex); _runtimeExecutor = runtimeExecutor; } #pragma mark - Internal Surface-dedicated Interface - (void)registerSurface:(RCTFabricSurface *)surface { [_surfaceRegistry registerSurface:surface]; RCTScheduler *scheduler = [self _scheduler]; if (scheduler) { [scheduler registerSurface:surface.surfaceHandler]; } } - (void)unregisterSurface:(RCTFabricSurface *)surface { RCTScheduler *scheduler = [self _scheduler]; if (scheduler) { [scheduler unregisterSurface:surface.surfaceHandler]; } [_surfaceRegistry unregisterSurface:surface]; } - (RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag { return [_surfaceRegistry surfaceForRootTag:rootTag]; } - (UIView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag { UIView *componentView = [_mountingManager.componentViewRegistry findComponentViewWithTag:tag]; return componentView; } - (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props { RCTScheduler *scheduler = [self _scheduler]; if (!scheduler) { return NO; } ReactTag tag = [reactTag integerValue]; UIView *componentView = [_mountingManager.componentViewRegistry findComponentViewWithTag:tag]; if (componentView == nil) { return NO; // This view probably isn't managed by Fabric } ComponentHandle handle = [[componentView class] componentDescriptorProvider].handle; auto *componentDescriptor = [scheduler findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN:handle]; if (!componentDescriptor) { return YES; } [_mountingManager synchronouslyUpdateViewOnUIThread:tag changedProps:props componentDescriptor:*componentDescriptor]; return YES; } - (void)setupAnimationDriverWithSurfaceHandler:(facebook::react::SurfaceHandler const &)surfaceHandler { [[self _scheduler] setupAnimationDriver:surfaceHandler]; } - (BOOL)suspend { std::lock_guard lock(_schedulerLifeCycleMutex); RCTScheduler *scheduler; { std::lock_guard accessLock(_schedulerAccessMutex); if (!_scheduler) { return NO; } scheduler = _scheduler; _scheduler = nil; } [self _stopAllSurfacesWithScheduler:scheduler]; return YES; } - (BOOL)resume { std::lock_guard lock(_schedulerLifeCycleMutex); RCTScheduler *scheduler; { std::lock_guard accessLock(_schedulerAccessMutex); if (_scheduler) { return NO; } scheduler = [self _createScheduler]; } [self _startAllSurfacesWithScheduler:scheduler]; { std::lock_guard accessLock(_schedulerAccessMutex); _scheduler = scheduler; } return YES; } #pragma mark - Private - (RCTScheduler *)_createScheduler { auto reactNativeConfig = _contextContainer->at>("ReactNativeConfig"); if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:scrollview_on_demand_mounting_ios")) { RCTExperimentSetOnDemandViewMounting(YES); } if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:disable_sending_scroll_events_to_paper")) { RCTExperimentSetSendScrollEventToPaper(NO); } if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:preemptive_view_allocation_disabled_ios")) { RCTExperimentSetPreemptiveViewAllocationDisabled(YES); } auto componentRegistryFactory = [factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)]( EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) { return [(RCTComponentViewFactory *)unwrapManagedObject(factory) createComponentDescriptorRegistryWithParameters:{eventDispatcher, contextContainer}]; }; auto runtimeExecutor = _runtimeExecutor; auto toolbox = SchedulerToolbox{}; toolbox.contextContainer = _contextContainer; toolbox.componentRegistryFactory = componentRegistryFactory; if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_runtimescheduler_ios")) { auto runtimeScheduler = std::make_shared(_runtimeExecutor); toolbox.runtimeScheduler = runtimeScheduler; runtimeExecutor = [runtimeScheduler](std::function &&callback) { runtimeScheduler->scheduleWork(std::move(callback)); }; } toolbox.runtimeExecutor = runtimeExecutor; toolbox.mainRunLoopObserverFactory = [](RunLoopObserver::Activity activities, RunLoopObserver::WeakOwner const &owner) { return std::make_unique(activities, owner); }; if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_background_executor_ios")) { toolbox.backgroundExecutor = RCTGetBackgroundExecutor(); } toolbox.synchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) { auto runLoopObserver = std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); return std::make_unique(std::move(runLoopObserver), runtimeExecutor); }; auto enableV2AsynchronousEventBeat = reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_asynchronous_event_beat_v2_ios"); toolbox.asynchronousEventBeatFactory = [runtimeExecutor, enableV2AsynchronousEventBeat]( EventBeat::SharedOwnerBox const &ownerBox) -> std::unique_ptr { auto runLoopObserver = std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); if (enableV2AsynchronousEventBeat) { return std::make_unique(std::move(runLoopObserver), runtimeExecutor); } else { return std::make_unique(std::move(runLoopObserver), runtimeExecutor); } }; RCTScheduler *scheduler = [[RCTScheduler alloc] initWithToolbox:toolbox]; scheduler.delegate = self; return scheduler; } - (void)_startAllSurfacesWithScheduler:(RCTScheduler *)scheduler { [_surfaceRegistry enumerateWithBlock:^(NSEnumerator *enumerator) { for (RCTFabricSurface *surface in enumerator) { [scheduler registerSurface:surface.surfaceHandler]; [surface start]; } }]; } - (void)_stopAllSurfacesWithScheduler:(RCTScheduler *)scheduler { [_surfaceRegistry enumerateWithBlock:^(NSEnumerator *enumerator) { for (RCTFabricSurface *surface in enumerator) { [surface stop]; [scheduler unregisterSurface:surface.surfaceHandler]; } }]; } #pragma mark - RCTSchedulerDelegate - (void)schedulerDidFinishTransaction:(MountingCoordinator::Shared const &)mountingCoordinator { [_mountingManager scheduleTransaction:mountingCoordinator]; } - (void)schedulerDidDispatchCommand:(ShadowView const &)shadowView commandName:(std::string const &)commandName args:(folly::dynamic const)args { ReactTag tag = shadowView.tag; NSString *commandStr = [[NSString alloc] initWithUTF8String:commandName.c_str()]; NSArray *argsArray = convertFollyDynamicToId(args); [self->_mountingManager dispatchCommand:tag commandName:commandStr args:argsArray]; } - (void)schedulerDidSendAccessibilityEvent:(const facebook::react::ShadowView &)shadowView eventType:(const std::string &)eventType { ReactTag tag = shadowView.tag; NSString *eventTypeStr = [[NSString alloc] initWithUTF8String:eventType.c_str()]; [self->_mountingManager sendAccessibilityEvent:tag eventType:eventTypeStr]; } - (void)schedulerDidSetIsJSResponder:(BOOL)isJSResponder blockNativeResponder:(BOOL)blockNativeResponder forShadowView:(facebook::react::ShadowView const &)shadowView; { [self->_mountingManager setIsJSResponder:isJSResponder blockNativeResponder:blockNativeResponder forShadowView:shadowView]; } - (void)addObserver:(id)observer { std::unique_lock lock(_observerListMutex); [self->_observers addObject:observer]; } - (void)removeObserver:(id)observer { std::unique_lock lock(_observerListMutex); [self->_observers removeObject:observer]; } #pragma mark - RCTMountingManagerDelegate - (void)mountingManager:(RCTMountingManager *)mountingManager willMountComponentsWithRootTag:(ReactTag)rootTag { RCTAssertMainQueue(); std::shared_lock lock(_observerListMutex); for (id observer in _observers) { if ([observer respondsToSelector:@selector(willMountComponentsWithRootTag:)]) { [observer willMountComponentsWithRootTag:rootTag]; } } } - (void)mountingManager:(RCTMountingManager *)mountingManager didMountComponentsWithRootTag:(ReactTag)rootTag { RCTAssertMainQueue(); std::shared_lock lock(_observerListMutex); for (id observer in _observers) { if ([observer respondsToSelector:@selector(didMountComponentsWithRootTag:)]) { [observer didMountComponentsWithRootTag:rootTag]; } } } @end