Bladeren bron

Invoke all commands on the main thread (#5857)

* Invoke all commands on the main thread

* Fix tests

* Remove flaky test
Yogev Ben David 4 jaren geleden
bovenliggende
commit
8843224a9a
No account linked to committer's email address

+ 23
- 0
lib/ios/RNNAssert.h Bestand weergeven

@@ -0,0 +1,23 @@
1
+#import <Foundation/Foundation.h>
2
+
3
+extern BOOL RNNIsMainQueue(void);
4
+
5
+#ifndef RNN_NSASSERT
6
+#define RNN_NSASSERT RCT_DEBUG
7
+#endif
8
+
9
+#ifndef NS_BLOCK_ASSERTIONS
10
+#define RNNAssert(condition, ...) do { \
11
+  if ((condition) == 0) { \
12
+    if (RNN_NSASSERT) { \
13
+      [[NSAssertionHandler currentHandler] handleFailureInFunction:(NSString * _Nonnull)@(__func__) \
14
+        file:(NSString * _Nonnull)@(__FILE__) lineNumber:__LINE__ description:__VA_ARGS__]; \
15
+    } \
16
+  } \
17
+} while (false)
18
+#else
19
+#define RNNAssert(condition, ...) do {} while (false)
20
+#endif
21
+
22
+#define RNNAssertMainQueue() RNNAssert(RNNIsMainQueue(), \
23
+@"This function must be called on the main queue")

+ 75
- 47
lib/ios/RNNBridgeModule.m Bestand weergeven

@@ -2,108 +2,136 @@
2 2
 #import "Constants.h"
3 3
 
4 4
 @implementation RNNBridgeModule {
5
-	RNNCommandsHandler* _commandsHandler;
5
+    RNNCommandsHandler* _commandsHandler;
6 6
 }
7 7
 @synthesize bridge = _bridge;
8 8
 RCT_EXPORT_MODULE();
9 9
 
10 10
 - (dispatch_queue_t)methodQueue {
11
-	return dispatch_get_main_queue();
11
+    return dispatch_get_main_queue();
12 12
 }
13 13
 
14 14
 -(instancetype)initWithCommandsHandler:(RNNCommandsHandler *)commandsHandler {
15
-	self = [super init];
16
-	_commandsHandler = commandsHandler;
17
-	return self;
15
+    self = [super init];
16
+    _commandsHandler = commandsHandler;
17
+    return self;
18 18
 }
19 19
 
20 20
 #pragma mark - JS interface
21 21
 
22 22
 RCT_EXPORT_METHOD(setRoot:(NSString*)commandId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
23
-	[_commandsHandler setRoot:layout commandId:commandId completion:^{
24
-		resolve(layout);
25
-	}];
23
+    RCTExecuteOnMainQueue(^{
24
+        [_commandsHandler setRoot:layout commandId:commandId completion:^{
25
+            resolve(layout);
26
+        }];
27
+    });
26 28
 }
27 29
 
28 30
 RCT_EXPORT_METHOD(mergeOptions:(NSString*)componentId options:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
29
-	[_commandsHandler mergeOptions:componentId options:options completion:^{
30
-		resolve(componentId);
31
-	}];
31
+    RCTExecuteOnMainQueue(^{
32
+        [_commandsHandler mergeOptions:componentId options:options completion:^{
33
+            resolve(componentId);
34
+        }];
35
+    });
32 36
 }
33 37
 
34 38
 RCT_EXPORT_METHOD(setDefaultOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
35
-	[_commandsHandler setDefaultOptions:options completion:^{
36
-		resolve(nil);
37
-	}];
39
+    RCTExecuteOnMainQueue(^{
40
+        [_commandsHandler setDefaultOptions:options completion:^{
41
+            resolve(nil);
42
+        }];
43
+    });
38 44
 }
39 45
 
40 46
 RCT_EXPORT_METHOD(push:(NSString*)commandId componentId:(NSString*)componentId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
41
-	[_commandsHandler push:componentId commandId:commandId layout:layout completion:^{
42
-		resolve(componentId);
43
-	} rejection:reject];
47
+    RCTExecuteOnMainQueue(^{
48
+        [_commandsHandler push:componentId commandId:commandId layout:layout completion:^{
49
+            resolve(componentId);
50
+        } rejection:reject];
51
+    });
44 52
 }
45 53
 
46 54
 RCT_EXPORT_METHOD(pop:(NSString*)commandId componentId:(NSString*)componentId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
47
-	[_commandsHandler pop:componentId commandId:commandId mergeOptions:(NSDictionary*)options completion:^{
48
-		resolve(componentId);
49
-	} rejection:reject];
55
+    RCTExecuteOnMainQueue(^{
56
+        [_commandsHandler pop:componentId commandId:commandId mergeOptions:(NSDictionary*)options completion:^{
57
+            resolve(componentId);
58
+        } rejection:reject];
59
+    });
50 60
 }
51 61
 
52 62
 RCT_EXPORT_METHOD(setStackRoot:(NSString*)commandId componentId:(NSString*)componentId children:(NSArray*)children resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
53
-	[_commandsHandler setStackRoot:componentId commandId:commandId children:children completion:^{
54
-		resolve(componentId);
55
-	} rejection:reject];
63
+    RCTExecuteOnMainQueue(^{
64
+        [_commandsHandler setStackRoot:componentId commandId:commandId children:children completion:^{
65
+            resolve(componentId);
66
+        } rejection:reject];
67
+    });
56 68
 }
57 69
 
58 70
 RCT_EXPORT_METHOD(popTo:(NSString*)commandId componentId:(NSString*)componentId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
59
-	[_commandsHandler popTo:componentId commandId:commandId mergeOptions:options completion:^{
60
-		resolve(componentId);
61
-	} rejection:reject];
71
+    RCTExecuteOnMainQueue(^{
72
+        [_commandsHandler popTo:componentId commandId:commandId mergeOptions:options completion:^{
73
+            resolve(componentId);
74
+        } rejection:reject];
75
+    });
62 76
 }
63 77
 
64 78
 RCT_EXPORT_METHOD(popToRoot:(NSString*)commandId componentId:(NSString*)componentId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
65
-	[_commandsHandler popToRoot:componentId commandId:commandId mergeOptions:options completion:^{
66
-		resolve(componentId);
67
-	} rejection:reject];
79
+    RCTExecuteOnMainQueue(^{
80
+        [_commandsHandler popToRoot:componentId commandId:commandId mergeOptions:options completion:^{
81
+            resolve(componentId);
82
+        } rejection:reject];
83
+    });
68 84
 }
69 85
 
70 86
 RCT_EXPORT_METHOD(showModal:(NSString*)commandId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
71
-	[_commandsHandler showModal:layout commandId:commandId completion:^(NSString *componentId) {
72
-		resolve(componentId);
73
-	}];
87
+    RCTExecuteOnMainQueue(^{
88
+        [_commandsHandler showModal:layout commandId:commandId completion:^(NSString *componentId) {
89
+            resolve(componentId);
90
+        }];
91
+    });
74 92
 }
75 93
 
76 94
 RCT_EXPORT_METHOD(dismissModal:(NSString*)commandId componentId:(NSString*)componentId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
77
-	[_commandsHandler dismissModal:componentId commandId:commandId mergeOptions:options completion:^{
78
-		resolve(componentId);
79
-	} rejection:reject];
95
+    RCTExecuteOnMainQueue(^{
96
+        [_commandsHandler dismissModal:componentId commandId:commandId mergeOptions:options completion:^{
97
+            resolve(componentId);
98
+        } rejection:reject];
99
+    });
80 100
 }
81 101
 
82 102
 RCT_EXPORT_METHOD(dismissAllModals:(NSString*)commandId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
83
-	[_commandsHandler dismissAllModals:options commandId:commandId completion:^{
84
-		resolve(nil);
85
-	}];
103
+    RCTExecuteOnMainQueue(^{
104
+        [_commandsHandler dismissAllModals:options commandId:commandId completion:^{
105
+            resolve(nil);
106
+        }];
107
+    });
86 108
 }
87 109
 
88 110
 RCT_EXPORT_METHOD(showOverlay:(NSString*)commandId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
89
-	[_commandsHandler showOverlay:layout commandId:commandId completion:^{
90
-		resolve(layout[@"id"]);
91
-	}];
111
+    RCTExecuteOnMainQueue(^{
112
+        [_commandsHandler showOverlay:layout commandId:commandId completion:^{
113
+            resolve(layout[@"id"]);
114
+        }];
115
+    });
92 116
 }
93 117
 
94 118
 RCT_EXPORT_METHOD(dismissOverlay:(NSString*)commandId componentId:(NSString*)componentId resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
95
-	[_commandsHandler dismissOverlay:componentId commandId:commandId completion:^{
96
-		resolve(@(1));
97
-	} rejection:reject];
119
+    RCTExecuteOnMainQueue(^{
120
+        [_commandsHandler dismissOverlay:componentId commandId:commandId completion:^{
121
+            resolve(@(1));
122
+        } rejection:reject];
123
+    });
98 124
 }
99 125
 
100 126
 RCT_EXPORT_METHOD(getLaunchArgs:(NSString*)commandId :(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
101
-	NSArray* args = [[NSProcessInfo processInfo] arguments];
102
-	resolve(args);
127
+    NSArray* args = [[NSProcessInfo processInfo] arguments];
128
+    resolve(args);
103 129
 }
104 130
 
105 131
 RCT_EXPORT_METHOD(getNavigationConstants:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
106
-	resolve([Constants getConstants]);
132
+    RCTExecuteOnMainQueue(^{
133
+        resolve([Constants getConstants]);
134
+    });
107 135
 }
108 136
 
109 137
 @end

+ 20
- 2
lib/ios/RNNCommandsHandler.m Bestand weergeven

@@ -7,6 +7,7 @@
7 7
 #import "UIViewController+LayoutProtocol.h"
8 8
 #import "RNNLayoutManager.h"
9 9
 #import "UIViewController+Utils.h"
10
+#import "RNNAssert.h"
10 11
 
11 12
 static NSString* const setRoot	= @"setRoot";
12 13
 static NSString* const setStackRoot	= @"setStackRoot";
@@ -52,6 +53,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
52 53
 
53 54
 - (void)setRoot:(NSDictionary*)layout commandId:(NSString*)commandId completion:(RNNTransitionCompletionBlock)completion {
54 55
 	[self assertReady];
56
+    RNNAssertMainQueue();
55 57
 	
56 58
 	if (@available(iOS 9, *)) {
57 59
 		if(_controllerFactory.defaultOptions.layout.direction.hasValue) {
@@ -85,6 +87,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
85 87
 
86 88
 - (void)mergeOptions:(NSString*)componentId options:(NSDictionary*)mergeOptions completion:(RNNTransitionCompletionBlock)completion {
87 89
 	[self assertReady];
90
+    RNNAssertMainQueue();
88 91
 	
89 92
 	UIViewController<RNNLayoutProtocol>* vc = [RNNLayoutManager findComponentForId:componentId];
90 93
 	RNNNavigationOptions* newOptions = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
@@ -100,6 +103,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
100 103
 
101 104
 - (void)setDefaultOptions:(NSDictionary*)optionsDict completion:(RNNTransitionCompletionBlock)completion {
102 105
 	[self assertReady];
106
+    RNNAssertMainQueue();
107
+    
103 108
 	RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initWithDict:optionsDict];
104 109
 	[_controllerFactory setDefaultOptions:defaultOptions];
105 110
 	
@@ -111,6 +116,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
111 116
 
112 117
 - (void)push:(NSString*)componentId commandId:(NSString*)commandId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
113 118
 	[self assertReady];
119
+    RNNAssertMainQueue();
114 120
 	
115 121
 	UIViewController *newVc = [_controllerFactory createLayout:layout];
116 122
 	UIViewController *fromVC = [RNNLayoutManager findComponentForId:componentId];
@@ -172,6 +178,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
172 178
 
173 179
 - (void)setStackRoot:(NSString*)componentId commandId:(NSString*)commandId children:(NSArray*)children completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
174 180
 	[self assertReady];
181
+    RNNAssertMainQueue();
175 182
 	
176 183
 	NSArray<UIViewController *> *childViewControllers = [_controllerFactory createChildrenLayout:children];
177 184
 	for (UIViewController<RNNLayoutProtocol>* viewController in childViewControllers) {
@@ -197,7 +204,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
197 204
 
198 205
 - (void)pop:(NSString*)componentId commandId:(NSString*)commandId mergeOptions:(NSDictionary*)mergeOptions completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
199 206
 	[self assertReady];
200
-	
207
+	RNNAssertMainQueue();
208
+    
201 209
 	RNNComponentViewController *vc = (RNNComponentViewController*)[RNNLayoutManager findComponentForId:componentId];
202 210
 	RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
203 211
 	[vc overrideOptions:options];
@@ -222,6 +230,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
222 230
 
223 231
 - (void)popTo:(NSString*)componentId commandId:(NSString*)commandId mergeOptions:(NSDictionary *)mergeOptions completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
224 232
 	[self assertReady];
233
+    RNNAssertMainQueue();
234
+    
225 235
 	RNNComponentViewController *vc = (RNNComponentViewController*)[RNNLayoutManager findComponentForId:componentId];
226 236
 	RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
227 237
 	[vc overrideOptions:options];
@@ -234,6 +244,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
234 244
 
235 245
 - (void)popToRoot:(NSString*)componentId commandId:(NSString*)commandId mergeOptions:(NSDictionary *)mergeOptions completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
236 246
 	[self assertReady];
247
+    RNNAssertMainQueue();
248
+    
237 249
 	RNNComponentViewController *vc = (RNNComponentViewController*)[RNNLayoutManager findComponentForId:componentId];
238 250
 	RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
239 251
 	[vc overrideOptions:options];
@@ -255,6 +267,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
255 267
 
256 268
 - (void)showModal:(NSDictionary*)layout commandId:(NSString *)commandId completion:(RNNTransitionWithComponentIdCompletionBlock)completion {
257 269
 	[self assertReady];
270
+    RNNAssertMainQueue();
258 271
 	
259 272
 	UIViewController *newVc = [_controllerFactory createLayout:layout];
260 273
 	
@@ -270,6 +283,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
270 283
 
271 284
 - (void)dismissModal:(NSString*)componentId commandId:(NSString*)commandId mergeOptions:(NSDictionary *)mergeOptions completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject {
272 285
 	[self assertReady];
286
+    RNNAssertMainQueue();
273 287
 	
274 288
 	UIViewController *modalToDismiss = (UIViewController *)[RNNLayoutManager findComponentForId:componentId];
275 289
 	
@@ -293,6 +307,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
293 307
 
294 308
 - (void)dismissAllModals:(NSDictionary *)mergeOptions commandId:(NSString*)commandId completion:(RNNTransitionCompletionBlock)completion {
295 309
 	[self assertReady];
310
+    RNNAssertMainQueue();
296 311
 	
297 312
 	[CATransaction begin];
298 313
 	[CATransaction setCompletionBlock:^{
@@ -307,7 +322,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
307 322
 
308 323
 - (void)showOverlay:(NSDictionary *)layout commandId:(NSString*)commandId completion:(RNNTransitionCompletionBlock)completion {
309 324
 	[self assertReady];
310
-	
325
+    RNNAssertMainQueue();
326
+    
311 327
 	UIViewController* overlayVC = [_controllerFactory createLayout:layout];
312 328
     [overlayVC setReactViewReadyCallback:^{UIWindow* overlayWindow = [[RNNOverlayWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
313 329
         overlayWindow.rootViewController = overlayVC;
@@ -327,6 +343,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
327 343
 
328 344
 - (void)dismissOverlay:(NSString*)componentId commandId:(NSString*)commandId completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject {
329 345
 	[self assertReady];
346
+    RNNAssertMainQueue();
347
+    
330 348
 	UIViewController* viewController = [RNNLayoutManager findComponentForId:componentId];
331 349
 	if (viewController) {
332 350
 		[_overlayManager dismissOverlay:viewController];

+ 12
- 0
lib/ios/RNNUtils.m Bestand weergeven

@@ -1,6 +1,7 @@
1 1
 #import "RNNUtils.h"
2 2
 
3 3
 @implementation RNNUtils
4
+
4 5
 +(double)getDoubleOrKey:(NSDictionary*)dict withKey:(NSString*)key withDefault:(double)defaultResult {
5 6
 	if ([dict objectForKey:key]) {
6 7
 		return [dict[key] doubleValue];
@@ -29,4 +30,15 @@
29 30
 	return [NSNumber numberWithLong:[[NSDate date] timeIntervalSince1970] * 1000];
30 31
 }
31 32
 
33
+
34
+BOOL RNNIsMainQueue() {
35
+  static void *mainQueueKey = &mainQueueKey;
36
+  static dispatch_once_t onceToken;
37
+  dispatch_once(&onceToken, ^{
38
+    dispatch_queue_set_specific(dispatch_get_main_queue(),
39
+                                mainQueueKey, mainQueueKey, NULL);
40
+  });
41
+  return dispatch_get_specific(mainQueueKey) == mainQueueKey;
42
+}
43
+
32 44
 @end

+ 2
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj Bestand weergeven

@@ -484,6 +484,7 @@
484 484
 		502F0E132178CF8200367CC3 /* UIViewController+RNNOptionsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+RNNOptionsTest.m"; sourceTree = "<group>"; };
485 485
 		502F0E152178D09600367CC3 /* RNNBasePresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBasePresenterTest.m; sourceTree = "<group>"; };
486 486
 		502F0E172179C39900367CC3 /* RNNTabBarPresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTabBarPresenterTest.m; sourceTree = "<group>"; };
487
+		5030B62D23D60002008F1642 /* RNNAssert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNAssert.h; sourceTree = "<group>"; };
487 488
 		50344D2623A03DB4004B6A7C /* BottomTabsAttachMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BottomTabsAttachMode.h; sourceTree = "<group>"; };
488 489
 		50344D2723A03DB4004B6A7C /* BottomTabsAttachMode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BottomTabsAttachMode.m; sourceTree = "<group>"; };
489 490
 		5038A372216CDDB6009280BC /* UIViewController+SideMenuController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+SideMenuController.h"; sourceTree = "<group>"; };
@@ -1305,6 +1306,7 @@
1305 1306
 				E5F6C39E22DB4D0E0093C2CE /* UIViewController+Utils.m */,
1306 1307
 				50DE2E43238EA14E005CD5F4 /* NSArray+utils.h */,
1307 1308
 				50DE2E44238EA14E005CD5F4 /* NSArray+utils.m */,
1309
+				5030B62D23D60002008F1642 /* RNNAssert.h */,
1308 1310
 			);
1309 1311
 			name = Utils;
1310 1312
 			sourceTree = "<group>";

+ 2
- 2
playground/ios/NavigationTests/RNNCommandsHandlerTest.m Bestand weergeven

@@ -83,7 +83,7 @@
83 83
 	return [[RNNComponentViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:nil presenter:[RNNComponentPresenter new] options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil];
84 84
 }
85 85
 
86
-- (void)testAssertReadyForEachMethodThrowsExceptoins {
86
+- (void)testAssertReadyForEachMethodThrowsExceptions {
87 87
 	NSArray* methods = [self getPublicMethodNamesForObject:self.uut];
88 88
 	[self.uut setReadyToReceiveCommands:false];
89 89
 	for (NSString* methodName in methods) {
@@ -94,7 +94,7 @@
94 94
 	}
95 95
 }
96 96
 
97
--(NSArray*) getPublicMethodNamesForObject:(NSObject*)obj{
97
+- (NSArray*)getPublicMethodNamesForObject:(NSObject*)obj {
98 98
 	NSMutableArray* skipMethods = [NSMutableArray new];
99 99
 	
100 100
 	[skipMethods addObject:@"initWithControllerFactory:eventEmitter:stackManager:modalManager:overlayManager:mainWindow:"];