/* * 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 "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 "PlatformRunLoopObserver.h" #import "RCTConversions.h" using namespace facebook; 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`. std::optional _bridgelessBindingsExecutor; // Only used for installing bindings. std::shared_mutex _observerListMutex; std::vector<__weak id> _observers; // Protected by `_observerListMutex`. } - (instancetype)initWithContextContainer:(ContextContainer::Shared)contextContainer runtimeExecutor:(RuntimeExecutor)runtimeExecutor bridgelessBindingsExecutor:(std::optional)bridgelessBindingsExecutor { if (self = [super init]) { assert(contextContainer && "RuntimeExecutor must be not null."); _runtimeExecutor = runtimeExecutor; _bridgelessBindingsExecutor = bridgelessBindingsExecutor; _contextContainer = contextContainer; _surfaceRegistry = [RCTSurfaceRegistry new]; _mountingManager = [RCTMountingManager new]; _mountingManager.contextContainer = contextContainer; _mountingManager.delegate = self; _scheduler = [self _createScheduler]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillTerminate) name:UIApplicationWillTerminateNotification object:nil]; } 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; _mountingManager.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]; } - (id)createFabricSurfaceForModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties { return [[RCTFabricSurface alloc] initWithSurfacePresenter:self moduleName:moduleName initialProperties:initialProperties]; } - (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("rn_convergence:dispatch_pointer_events")) { RCTSetDispatchW3CPointerEvents(YES); } if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_cpp_props_iterator_setter_ios")) { CoreFeatures::enablePropIteratorSetter = true; } if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:use_native_state")) { CoreFeatures::useNativeState = true; } if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_nstextstorage_caching")) { CoreFeatures::cacheNSTextStorage = true; } if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:cancel_image_downloads_on_recycle")) { CoreFeatures::cancelImageDownloadsOnRecycle = true; } 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; auto weakRuntimeScheduler = _contextContainer->find>("RuntimeScheduler"); auto runtimeScheduler = weakRuntimeScheduler.has_value() ? weakRuntimeScheduler.value().lock() : nullptr; if (runtimeScheduler) { runtimeExecutor = [runtimeScheduler](std::function &&callback) { runtimeScheduler->scheduleWork(std::move(callback)); }; } toolbox.runtimeExecutor = runtimeExecutor; toolbox.bridgelessBindingsExecutor = _bridgelessBindingsExecutor; 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, runtimeScheduler = runtimeScheduler](EventBeat::SharedOwnerBox const &ownerBox) { auto runLoopObserver = std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); return std::make_unique(std::move(runLoopObserver), runtimeExecutor, runtimeScheduler); }; toolbox.asynchronousEventBeatFactory = [runtimeExecutor](EventBeat::SharedOwnerBox const &ownerBox) -> std::unique_ptr { auto runLoopObserver = std::make_unique(RunLoopObserver::Activity::BeforeWaiting, ownerBox->owner); 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]; } }]; } - (void)_applicationWillTerminate { [self suspend]; } #pragma mark - RCTSchedulerDelegate - (void)schedulerDidFinishTransaction:(MountingCoordinator::Shared)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); [_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()]; [_mountingManager sendAccessibilityEvent:tag eventType:eventTypeStr]; } - (void)schedulerDidSetIsJSResponder:(BOOL)isJSResponder blockNativeResponder:(BOOL)blockNativeResponder forShadowView:(facebook::react::ShadowView const &)shadowView; { [_mountingManager setIsJSResponder:isJSResponder blockNativeResponder:blockNativeResponder forShadowView:shadowView]; } - (void)addObserver:(id)observer { std::unique_lock lock(_observerListMutex); _observers.push_back(observer); } - (void)removeObserver:(id)observer { std::unique_lock lock(_observerListMutex); std::vector<__weak id>::const_iterator it = std::find(_observers.begin(), _observers.end(), observer); if (it != _observers.end()) { _observers.erase(it); } } #pragma mark - RCTMountingManagerDelegate - (void)mountingManager:(RCTMountingManager *)mountingManager willMountComponentsWithRootTag:(ReactTag)rootTag { RCTAssertMainQueue(); NSArray> *observersCopy; { std::shared_lock lock(_observerListMutex); observersCopy = [self _getObservers]; } for (id observer in observersCopy) { if ([observer respondsToSelector:@selector(willMountComponentsWithRootTag:)]) { [observer willMountComponentsWithRootTag:rootTag]; } } } - (void)mountingManager:(RCTMountingManager *)mountingManager didMountComponentsWithRootTag:(ReactTag)rootTag { RCTAssertMainQueue(); NSArray> *observersCopy; { std::shared_lock lock(_observerListMutex); observersCopy = [self _getObservers]; } for (id observer in observersCopy) { if ([observer respondsToSelector:@selector(didMountComponentsWithRootTag:)]) { [observer didMountComponentsWithRootTag:rootTag]; } } } - (NSArray> *)_getObservers { NSMutableArray> *observersCopy = [NSMutableArray new]; for (id observer : _observers) { if (observer) { [observersCopy addObject:observer]; } } return observersCopy; } @end