RCTSurface.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTSurface.h"
  8. #import "RCTSurfaceView+Internal.h"
  9. #import <mutex>
  10. #import <stdatomic.h>
  11. #import "RCTAssert.h"
  12. #import "RCTBridge+Private.h"
  13. #import "RCTBridge.h"
  14. #import "RCTShadowView+Layout.h"
  15. #import "RCTSurfaceDelegate.h"
  16. #import "RCTSurfaceRootShadowView.h"
  17. #import "RCTSurfaceRootShadowViewDelegate.h"
  18. #import "RCTSurfaceRootView.h"
  19. #import "RCTSurfaceView.h"
  20. #import "RCTTouchHandler.h"
  21. #import "RCTUIManager.h"
  22. #import "RCTUIManagerObserverCoordinator.h"
  23. #import "RCTUIManagerUtils.h"
  24. @interface RCTSurface () <RCTSurfaceRootShadowViewDelegate, RCTUIManagerObserver>
  25. @end
  26. @implementation RCTSurface {
  27. // Immutable
  28. RCTBridge *_bridge;
  29. NSString *_moduleName;
  30. NSNumber *_rootViewTag;
  31. // Protected by the `_mutex`
  32. std::mutex _mutex;
  33. RCTBridge *_batchedBridge;
  34. RCTSurfaceStage _stage;
  35. NSDictionary *_properties;
  36. CGSize _minimumSize;
  37. CGSize _maximumSize;
  38. CGSize _intrinsicSize;
  39. RCTUIManagerMountingBlock _mountingBlock;
  40. // The Main thread only
  41. RCTSurfaceView *_Nullable _view;
  42. RCTTouchHandler *_Nullable _touchHandler;
  43. // Semaphores
  44. dispatch_semaphore_t _rootShadowViewDidStartRenderingSemaphore;
  45. dispatch_semaphore_t _rootShadowViewDidStartLayingOutSemaphore;
  46. dispatch_semaphore_t _uiManagerDidPerformMountingSemaphore;
  47. // Atomics
  48. atomic_bool _waitingForMountingStageOnMainQueue;
  49. }
  50. - (instancetype)initWithBridge:(RCTBridge *)bridge
  51. moduleName:(NSString *)moduleName
  52. initialProperties:(NSDictionary *)initialProperties
  53. {
  54. RCTAssert(bridge.valid, @"Valid bridge is required to instantiate `RCTSurface`.");
  55. if (self = [super init]) {
  56. _bridge = bridge;
  57. _batchedBridge = [_bridge batchedBridge] ?: _bridge;
  58. _moduleName = moduleName;
  59. _properties = [initialProperties copy];
  60. _rootViewTag = RCTAllocateRootViewTag();
  61. _rootShadowViewDidStartRenderingSemaphore = dispatch_semaphore_create(0);
  62. _rootShadowViewDidStartLayingOutSemaphore = dispatch_semaphore_create(0);
  63. _uiManagerDidPerformMountingSemaphore = dispatch_semaphore_create(0);
  64. _minimumSize = CGSizeZero;
  65. _maximumSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
  66. [[NSNotificationCenter defaultCenter] addObserver:self
  67. selector:@selector(handleBridgeWillLoadJavaScriptNotification:)
  68. name:RCTJavaScriptWillStartLoadingNotification
  69. object:_bridge];
  70. [[NSNotificationCenter defaultCenter] addObserver:self
  71. selector:@selector(handleBridgeDidLoadJavaScriptNotification:)
  72. name:RCTJavaScriptDidLoadNotification
  73. object:_bridge];
  74. _stage = RCTSurfaceStageSurfaceDidInitialize;
  75. if (!bridge.loading) {
  76. _stage = _stage | RCTSurfaceStageBridgeDidLoad;
  77. }
  78. [_bridge.uiManager.observerCoordinator addObserver:self];
  79. [self _registerRootView];
  80. [self _run];
  81. }
  82. return self;
  83. }
  84. - (void)dealloc
  85. {
  86. [self _stop];
  87. }
  88. #pragma mark - Immutable Properties (no need to enforce synchronization)
  89. - (RCTBridge *)bridge
  90. {
  91. return _bridge;
  92. }
  93. - (NSString *)moduleName
  94. {
  95. return _moduleName;
  96. }
  97. - (NSNumber *)rootViewTag
  98. {
  99. return _rootViewTag;
  100. }
  101. #pragma mark - Convenience Internal Thread-Safe Properties
  102. - (RCTBridge *)_batchedBridge
  103. {
  104. std::lock_guard<std::mutex> lock(_mutex);
  105. return _batchedBridge;
  106. }
  107. - (RCTUIManager *)_uiManager
  108. {
  109. return self._batchedBridge.uiManager;
  110. }
  111. #pragma mark - Main-Threaded Routines
  112. - (RCTSurfaceView *)view
  113. {
  114. RCTAssertMainQueue();
  115. if (!_view) {
  116. _view = [[RCTSurfaceView alloc] initWithSurface:self];
  117. _touchHandler = [[RCTTouchHandler alloc] initWithBridge:self.bridge];
  118. [_touchHandler attachToView:_view];
  119. [self _mountRootViewIfNeeded];
  120. }
  121. return _view;
  122. }
  123. - (void)_mountRootViewIfNeeded
  124. {
  125. RCTAssertMainQueue();
  126. RCTSurfaceView *view = self->_view;
  127. if (!view) {
  128. return;
  129. }
  130. RCTSurfaceRootView *rootView = (RCTSurfaceRootView *)[self._uiManager viewForReactTag:self->_rootViewTag];
  131. if (!rootView) {
  132. return;
  133. }
  134. RCTAssert(
  135. [rootView isKindOfClass:[RCTSurfaceRootView class]],
  136. @"Received root view is not an instance of `RCTSurfaceRootView`.");
  137. if (rootView.superview != view) {
  138. view.rootView = rootView;
  139. }
  140. }
  141. #pragma mark - Bridge Events
  142. - (void)handleBridgeWillLoadJavaScriptNotification:(__unused NSNotification *)notification
  143. {
  144. RCTAssertMainQueue();
  145. // Reset states because the bridge is reloading. This is similar to initialization phase.
  146. _stage = RCTSurfaceStageSurfaceDidInitialize;
  147. _view = nil;
  148. _touchHandler = nil;
  149. [self _setStage:RCTSurfaceStageBridgeDidLoad];
  150. }
  151. - (void)handleBridgeDidLoadJavaScriptNotification:(NSNotification *)notification
  152. {
  153. RCTAssertMainQueue();
  154. [self _setStage:RCTSurfaceStageModuleDidLoad];
  155. RCTBridge *bridge = notification.userInfo[@"bridge"];
  156. BOOL isRerunNeeded = NO;
  157. {
  158. std::lock_guard<std::mutex> lock(_mutex);
  159. if (bridge != _batchedBridge) {
  160. _batchedBridge = bridge;
  161. isRerunNeeded = YES;
  162. }
  163. }
  164. if (isRerunNeeded) {
  165. [self _registerRootView];
  166. [self _run];
  167. }
  168. }
  169. #pragma mark - Stage management
  170. - (RCTSurfaceStage)stage
  171. {
  172. std::lock_guard<std::mutex> lock(_mutex);
  173. return _stage;
  174. }
  175. - (void)_setStage:(RCTSurfaceStage)stage
  176. {
  177. RCTSurfaceStage updatedStage;
  178. {
  179. std::lock_guard<std::mutex> lock(_mutex);
  180. if (_stage & stage) {
  181. return;
  182. }
  183. updatedStage = (RCTSurfaceStage)(_stage | stage);
  184. _stage = updatedStage;
  185. }
  186. [self _propagateStageChange:updatedStage];
  187. }
  188. - (void)_propagateStageChange:(RCTSurfaceStage)stage
  189. {
  190. // Updating the `view`
  191. RCTExecuteOnMainQueue(^{
  192. self->_view.stage = stage;
  193. });
  194. // Notifying the `delegate`
  195. id<RCTSurfaceDelegate> delegate = self.delegate;
  196. if ([delegate respondsToSelector:@selector(surface:didChangeStage:)]) {
  197. [delegate surface:self didChangeStage:stage];
  198. }
  199. }
  200. #pragma mark - Properties Management
  201. - (NSDictionary *)properties
  202. {
  203. std::lock_guard<std::mutex> lock(_mutex);
  204. return _properties;
  205. }
  206. - (void)setProperties:(NSDictionary *)properties
  207. {
  208. {
  209. std::lock_guard<std::mutex> lock(_mutex);
  210. if ([properties isEqualToDictionary:_properties]) {
  211. return;
  212. }
  213. _properties = [properties copy];
  214. }
  215. [self _run];
  216. }
  217. #pragma mark - Running
  218. - (void)_run
  219. {
  220. RCTBridge *batchedBridge;
  221. NSDictionary *properties;
  222. {
  223. std::lock_guard<std::mutex> lock(_mutex);
  224. batchedBridge = _batchedBridge;
  225. properties = _properties;
  226. }
  227. if (!batchedBridge.valid) {
  228. return;
  229. }
  230. NSDictionary *applicationParameters = @{
  231. @"rootTag" : _rootViewTag,
  232. @"initialProps" : properties,
  233. };
  234. RCTLogInfo(@"Running surface %@ (%@)", _moduleName, applicationParameters);
  235. [self mountReactComponentWithBridge:batchedBridge moduleName:_moduleName params:applicationParameters];
  236. [self _setStage:RCTSurfaceStageSurfaceDidRun];
  237. }
  238. - (void)_stop
  239. {
  240. [self unmountReactComponentWithBridge:self._batchedBridge rootViewTag:self->_rootViewTag];
  241. }
  242. - (void)_registerRootView
  243. {
  244. RCTBridge *batchedBridge;
  245. CGSize minimumSize;
  246. CGSize maximumSize;
  247. {
  248. std::lock_guard<std::mutex> lock(_mutex);
  249. batchedBridge = _batchedBridge;
  250. minimumSize = _minimumSize;
  251. maximumSize = _maximumSize;
  252. }
  253. RCTUIManager *uiManager = batchedBridge.uiManager;
  254. // If we are on the main queue now, we have to proceed synchronously.
  255. // Otherwise, we cannot perform synchronous waiting for some stages later.
  256. (RCTIsMainQueue() ? RCTUnsafeExecuteOnUIManagerQueueSync : RCTExecuteOnUIManagerQueue)(^{
  257. [uiManager registerRootViewTag:self->_rootViewTag];
  258. RCTSurfaceRootShadowView *rootShadowView =
  259. (RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag];
  260. RCTAssert(
  261. [rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]],
  262. @"Received shadow view is not an instance of `RCTSurfaceRootShadowView`.");
  263. [rootShadowView setMinimumSize:minimumSize maximumSize:maximumSize];
  264. rootShadowView.delegate = self;
  265. });
  266. }
  267. #pragma mark - Layout
  268. - (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
  269. {
  270. RCTUIManager *uiManager = self._uiManager;
  271. __block CGSize fittingSize;
  272. RCTUnsafeExecuteOnUIManagerQueueSync(^{
  273. RCTSurfaceRootShadowView *rootShadowView =
  274. (RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag];
  275. RCTAssert(
  276. [rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]],
  277. @"Received shadow view is not an instance of `RCTSurfaceRootShadowView`.");
  278. fittingSize = [rootShadowView sizeThatFitsMinimumSize:minimumSize maximumSize:maximumSize];
  279. });
  280. return fittingSize;
  281. }
  282. #pragma mark - Size Constraints
  283. - (void)setSize:(CGSize)size
  284. {
  285. [self setMinimumSize:size maximumSize:size];
  286. }
  287. - (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
  288. {
  289. {
  290. std::lock_guard<std::mutex> lock(_mutex);
  291. if (CGSizeEqualToSize(minimumSize, _minimumSize) && CGSizeEqualToSize(maximumSize, _maximumSize)) {
  292. return;
  293. }
  294. _maximumSize = maximumSize;
  295. _minimumSize = minimumSize;
  296. }
  297. RCTUIManager *uiManager = self._uiManager;
  298. RCTUnsafeExecuteOnUIManagerQueueSync(^{
  299. RCTSurfaceRootShadowView *rootShadowView =
  300. (RCTSurfaceRootShadowView *)[uiManager shadowViewForReactTag:self->_rootViewTag];
  301. RCTAssert(
  302. [rootShadowView isKindOfClass:[RCTSurfaceRootShadowView class]],
  303. @"Received shadow view is not an instance of `RCTSurfaceRootShadowView`.");
  304. [rootShadowView setMinimumSize:minimumSize maximumSize:maximumSize];
  305. [uiManager setNeedsLayout];
  306. });
  307. }
  308. - (CGSize)minimumSize
  309. {
  310. std::lock_guard<std::mutex> lock(_mutex);
  311. return _minimumSize;
  312. }
  313. - (CGSize)maximumSize
  314. {
  315. std::lock_guard<std::mutex> lock(_mutex);
  316. return _maximumSize;
  317. }
  318. #pragma mark - intrinsicSize
  319. - (void)setIntrinsicSize:(CGSize)intrinsicSize
  320. {
  321. {
  322. std::lock_guard<std::mutex> lock(_mutex);
  323. if (CGSizeEqualToSize(intrinsicSize, _intrinsicSize)) {
  324. return;
  325. }
  326. _intrinsicSize = intrinsicSize;
  327. }
  328. // Notifying `delegate`
  329. id<RCTSurfaceDelegate> delegate = self.delegate;
  330. if ([delegate respondsToSelector:@selector(surface:didChangeIntrinsicSize:)]) {
  331. [delegate surface:self didChangeIntrinsicSize:intrinsicSize];
  332. }
  333. }
  334. - (CGSize)intrinsicSize
  335. {
  336. std::lock_guard<std::mutex> lock(_mutex);
  337. return _intrinsicSize;
  338. }
  339. #pragma mark - Synchronous Waiting
  340. - (BOOL)synchronouslyWaitForStage:(RCTSurfaceStage)stage timeout:(NSTimeInterval)timeout
  341. {
  342. if (RCTIsUIManagerQueue()) {
  343. RCTLogInfo(@"Synchronous waiting is not supported on UIManager queue.");
  344. return NO;
  345. }
  346. if (RCTIsMainQueue() && (stage & RCTSurfaceStageSurfaceDidInitialMounting)) {
  347. // All main-threaded execution (especially mounting process) has to be
  348. // intercepted, captured and performed synchronously at the end of this method
  349. // right after the semaphore signals.
  350. // Atomic variant of `_waitingForMountingStageOnMainQueue = YES;`
  351. atomic_fetch_or(&_waitingForMountingStageOnMainQueue, 1);
  352. }
  353. dispatch_semaphore_t semaphore;
  354. switch (stage) {
  355. case RCTSurfaceStageSurfaceDidInitialLayout:
  356. semaphore = _rootShadowViewDidStartLayingOutSemaphore;
  357. break;
  358. case RCTSurfaceStageSurfaceDidInitialRendering:
  359. semaphore = _rootShadowViewDidStartRenderingSemaphore;
  360. break;
  361. case RCTSurfaceStageSurfaceDidInitialMounting:
  362. semaphore = _uiManagerDidPerformMountingSemaphore;
  363. break;
  364. default:
  365. RCTAssert(
  366. NO,
  367. @"Only waiting for `RCTSurfaceStageSurfaceDidInitialRendering`, `RCTSurfaceStageSurfaceDidInitialLayout` and `RCTSurfaceStageSurfaceDidInitialMounting` stages are supported.");
  368. }
  369. BOOL timeoutOccurred = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC));
  370. // Atomic equivalent of `_waitingForMountingStageOnMainQueue = NO;`.
  371. atomic_fetch_and(&_waitingForMountingStageOnMainQueue, 0);
  372. if (!timeoutOccurred) {
  373. // Balancing the semaphore.
  374. // Note: `dispatch_semaphore_wait` reverts the decrement in case when timeout occurred.
  375. dispatch_semaphore_signal(semaphore);
  376. }
  377. if (RCTIsMainQueue() && (stage & RCTSurfaceStageSurfaceDidInitialMounting)) {
  378. // Time to apply captured mounting block.
  379. RCTUIManagerMountingBlock mountingBlock;
  380. {
  381. std::lock_guard<std::mutex> lock(_mutex);
  382. mountingBlock = _mountingBlock;
  383. _mountingBlock = nil;
  384. }
  385. if (mountingBlock) {
  386. mountingBlock();
  387. [self _mountRootViewIfNeeded];
  388. }
  389. }
  390. return !timeoutOccurred;
  391. }
  392. #pragma mark - RCTSurfaceRootShadowViewDelegate
  393. - (void)rootShadowView:(__unused RCTRootShadowView *)rootShadowView didChangeIntrinsicSize:(CGSize)intrinsicSize
  394. {
  395. self.intrinsicSize = intrinsicSize;
  396. }
  397. - (void)rootShadowViewDidStartRendering:(__unused RCTSurfaceRootShadowView *)rootShadowView
  398. {
  399. [self _setStage:RCTSurfaceStageSurfaceDidInitialRendering];
  400. dispatch_semaphore_signal(_rootShadowViewDidStartRenderingSemaphore);
  401. }
  402. - (void)rootShadowViewDidStartLayingOut:(__unused RCTSurfaceRootShadowView *)rootShadowView
  403. {
  404. [self _setStage:RCTSurfaceStageSurfaceDidInitialLayout];
  405. dispatch_semaphore_signal(_rootShadowViewDidStartLayingOutSemaphore);
  406. RCTExecuteOnMainQueue(^{
  407. // Rendering is happening, let's mount `rootView` into `view` if we already didn't do this.
  408. [self _mountRootViewIfNeeded];
  409. });
  410. }
  411. #pragma mark - RCTUIManagerObserver
  412. - (BOOL)uiManager:(__unused RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block
  413. {
  414. if (atomic_load(&_waitingForMountingStageOnMainQueue) && (self.stage & RCTSurfaceStageSurfaceDidInitialLayout)) {
  415. // Atomic equivalent of `_waitingForMountingStageOnMainQueue = NO;`.
  416. atomic_fetch_and(&_waitingForMountingStageOnMainQueue, 0);
  417. {
  418. std::lock_guard<std::mutex> lock(_mutex);
  419. _mountingBlock = block;
  420. }
  421. return YES;
  422. }
  423. return NO;
  424. }
  425. - (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager
  426. {
  427. if (self.stage & RCTSurfaceStageSurfaceDidInitialLayout) {
  428. [self _setStage:RCTSurfaceStageSurfaceDidInitialMounting];
  429. dispatch_semaphore_signal(_uiManagerDidPerformMountingSemaphore);
  430. // No need to listen to UIManager anymore.
  431. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  432. [self->_bridge.uiManager.observerCoordinator removeObserver:self];
  433. });
  434. }
  435. }
  436. - (BOOL)start
  437. {
  438. // Does nothing.
  439. // The Start&Stop feature is not implemented for regular Surface yet.
  440. return YES;
  441. }
  442. - (BOOL)stop
  443. {
  444. // Does nothing.
  445. // The Start&Stop feature is not implemented for regular Surface yet.
  446. return YES;
  447. }
  448. #pragma mark - Mounting/Unmounting of React components
  449. - (void)mountReactComponentWithBridge:(RCTBridge *)bridge
  450. moduleName:(NSString *)moduleName
  451. params:(NSDictionary *)params
  452. {
  453. [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[ moduleName, params ] completion:NULL];
  454. }
  455. - (void)unmountReactComponentWithBridge:(RCTBridge *)bridge rootViewTag:(NSNumber *)rootViewTag
  456. {
  457. [bridge enqueueJSCall:@"AppRegistry"
  458. method:@"unmountApplicationComponentAtRootTag"
  459. args:@[ rootViewTag ]
  460. completion:NULL];
  461. }
  462. @end