FBConfiguration.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "FBConfiguration.h"
  10. #import "AXSettings.h"
  11. #import "UIKeyboardImpl.h"
  12. #import "TIPreferencesController.h"
  13. #include <dlfcn.h>
  14. #import <UIKit/UIKit.h>
  15. #include "TargetConditionals.h"
  16. #import "FBXCodeCompatibility.h"
  17. #import "XCAXClient_iOS+FBSnapshotReqParams.h"
  18. #import "XCTestPrivateSymbols.h"
  19. #import "XCTestConfiguration.h"
  20. #import "XCUIApplication+FBUIInterruptions.h"
  21. static NSUInteger const DefaultStartingPort = 8100;
  22. static NSUInteger const DefaultMjpegServerPort = 9100;
  23. static NSUInteger const DefaultPortRange = 100;
  24. static char const *const controllerPrefBundlePath = "/System/Library/PrivateFrameworks/TextInput.framework/TextInput";
  25. static NSString *const controllerClassName = @"TIPreferencesController";
  26. static NSString *const FBKeyboardAutocorrectionKey = @"KeyboardAutocorrection";
  27. static NSString *const FBKeyboardPredictionKey = @"KeyboardPrediction";
  28. static NSString *const axSettingsClassName = @"AXSettings";
  29. static BOOL FBShouldUseTestManagerForVisibilityDetection = NO;
  30. static BOOL FBShouldUseSingletonTestManager = YES;
  31. static BOOL FBShouldRespectSystemAlerts = NO;
  32. static CGFloat FBMjpegScalingFactor = 100.0;
  33. static BOOL FBMjpegShouldFixOrientation = NO;
  34. static NSUInteger FBMjpegServerScreenshotQuality = 25;
  35. static NSUInteger FBMjpegServerFramerate = 10;
  36. // Session-specific settings
  37. static BOOL FBShouldTerminateApp;
  38. static NSNumber* FBMaxTypingFrequency;
  39. static NSUInteger FBScreenshotQuality;
  40. static BOOL FBShouldUseFirstMatch;
  41. static BOOL FBShouldBoundElementsByIndex;
  42. static BOOL FBIncludeNonModalElements;
  43. static NSString *FBAcceptAlertButtonSelector;
  44. static NSString *FBDismissAlertButtonSelector;
  45. static NSString *FBAutoClickAlertSelector;
  46. static NSTimeInterval FBWaitForIdleTimeout;
  47. static NSTimeInterval FBAnimationCoolOffTimeout;
  48. static BOOL FBShouldUseCompactResponses;
  49. static NSString *FBElementResponseAttributes;
  50. static BOOL FBUseClearTextShortcut;
  51. static BOOL FBLimitXpathContextScope = YES;
  52. #if !TARGET_OS_TV
  53. static UIInterfaceOrientation FBScreenshotOrientation;
  54. #endif
  55. static BOOL FBShouldIncludeHittableInPageSource = NO;
  56. static BOOL FBShouldIncludeNativeFrameInPageSource = NO;
  57. static BOOL FBShouldIncludeMinMaxValueInPageSource = NO;
  58. @implementation FBConfiguration
  59. + (NSUInteger)defaultTypingFrequency
  60. {
  61. NSInteger defaultFreq = [[NSUserDefaults standardUserDefaults]
  62. integerForKey:@"com.apple.xctest.iOSMaximumTypingFrequency"];
  63. return defaultFreq > 0 ? defaultFreq : 60;
  64. }
  65. + (void)initialize
  66. {
  67. [FBConfiguration resetSessionSettings];
  68. }
  69. #pragma mark Public
  70. + (void)disableRemoteQueryEvaluation
  71. {
  72. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"XCTDisableRemoteQueryEvaluation"];
  73. }
  74. + (void)disableApplicationUIInterruptionsHandling
  75. {
  76. [XCUIApplication fb_disableUIInterruptionsHandling];
  77. }
  78. + (void)enableXcTestDebugLogs
  79. {
  80. ((XCTestConfiguration *)XCTestConfiguration.activeTestConfiguration).emitOSLogs = YES;
  81. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"XCTEmitOSLogs"];
  82. }
  83. + (void)disableAttributeKeyPathAnalysis
  84. {
  85. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"XCTDisableAttributeKeyPathAnalysis"];
  86. }
  87. + (void)disableScreenshots
  88. {
  89. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"DisableScreenshots"];
  90. }
  91. + (void)enableScreenshots
  92. {
  93. [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"DisableScreenshots"];
  94. }
  95. + (void)disableScreenRecordings
  96. {
  97. [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"DisableDiagnosticScreenRecordings"];
  98. }
  99. + (void)enableScreenRecordings
  100. {
  101. [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"DisableDiagnosticScreenRecordings"];
  102. }
  103. + (NSRange)bindingPortRange
  104. {
  105. // 'WebDriverAgent --port 8080' can be passed via the arguments to the process
  106. if (self.bindingPortRangeFromArguments.location != NSNotFound) {
  107. return self.bindingPortRangeFromArguments;
  108. }
  109. // Existence of USE_PORT in the environment implies the port range is managed by the launching process.
  110. if (NSProcessInfo.processInfo.environment[@"USE_PORT"] &&
  111. [NSProcessInfo.processInfo.environment[@"USE_PORT"] length] > 0) {
  112. return NSMakeRange([NSProcessInfo.processInfo.environment[@"USE_PORT"] integerValue] , 1);
  113. }
  114. return NSMakeRange(DefaultStartingPort, DefaultPortRange);
  115. }
  116. + (NSInteger)mjpegServerPort
  117. {
  118. if (self.mjpegServerPortFromArguments != NSNotFound) {
  119. return self.mjpegServerPortFromArguments;
  120. }
  121. if (NSProcessInfo.processInfo.environment[@"MJPEG_SERVER_PORT"] &&
  122. [NSProcessInfo.processInfo.environment[@"MJPEG_SERVER_PORT"] length] > 0) {
  123. return [NSProcessInfo.processInfo.environment[@"MJPEG_SERVER_PORT"] integerValue];
  124. }
  125. return DefaultMjpegServerPort;
  126. }
  127. + (CGFloat)mjpegScalingFactor
  128. {
  129. return FBMjpegScalingFactor;
  130. }
  131. + (void)setMjpegScalingFactor:(CGFloat)scalingFactor {
  132. FBMjpegScalingFactor = scalingFactor;
  133. }
  134. + (BOOL)mjpegShouldFixOrientation
  135. {
  136. return FBMjpegShouldFixOrientation;
  137. }
  138. + (void)setMjpegShouldFixOrientation:(BOOL)enabled {
  139. FBMjpegShouldFixOrientation = enabled;
  140. }
  141. + (BOOL)verboseLoggingEnabled
  142. {
  143. return [NSProcessInfo.processInfo.environment[@"VERBOSE_LOGGING"] boolValue];
  144. }
  145. + (void)setShouldUseTestManagerForVisibilityDetection:(BOOL)value
  146. {
  147. FBShouldUseTestManagerForVisibilityDetection = value;
  148. }
  149. + (BOOL)shouldUseTestManagerForVisibilityDetection
  150. {
  151. return FBShouldUseTestManagerForVisibilityDetection;
  152. }
  153. + (void)setShouldUseCompactResponses:(BOOL)value
  154. {
  155. FBShouldUseCompactResponses = value;
  156. }
  157. + (BOOL)shouldUseCompactResponses
  158. {
  159. return FBShouldUseCompactResponses;
  160. }
  161. + (void)setShouldTerminateApp:(BOOL)value
  162. {
  163. FBShouldTerminateApp = value;
  164. }
  165. + (BOOL)shouldTerminateApp
  166. {
  167. return FBShouldTerminateApp;
  168. }
  169. + (void)setElementResponseAttributes:(NSString *)value
  170. {
  171. FBElementResponseAttributes = value;
  172. }
  173. + (NSString *)elementResponseAttributes
  174. {
  175. return FBElementResponseAttributes;
  176. }
  177. + (void)setMaxTypingFrequency:(NSUInteger)value
  178. {
  179. FBMaxTypingFrequency = @(value);
  180. }
  181. + (NSUInteger)maxTypingFrequency
  182. {
  183. if (nil == FBMaxTypingFrequency) {
  184. return [self defaultTypingFrequency];
  185. }
  186. return FBMaxTypingFrequency.integerValue <= 0
  187. ? [self defaultTypingFrequency]
  188. : FBMaxTypingFrequency.integerValue;
  189. }
  190. + (void)setShouldUseSingletonTestManager:(BOOL)value
  191. {
  192. FBShouldUseSingletonTestManager = value;
  193. }
  194. + (BOOL)shouldUseSingletonTestManager
  195. {
  196. return FBShouldUseSingletonTestManager;
  197. }
  198. + (NSUInteger)mjpegServerFramerate
  199. {
  200. return FBMjpegServerFramerate;
  201. }
  202. + (void)setMjpegServerFramerate:(NSUInteger)framerate
  203. {
  204. FBMjpegServerFramerate = framerate;
  205. }
  206. + (NSUInteger)mjpegServerScreenshotQuality
  207. {
  208. return FBMjpegServerScreenshotQuality;
  209. }
  210. + (void)setMjpegServerScreenshotQuality:(NSUInteger)quality
  211. {
  212. FBMjpegServerScreenshotQuality = quality;
  213. }
  214. + (NSUInteger)screenshotQuality
  215. {
  216. return FBScreenshotQuality;
  217. }
  218. + (void)setScreenshotQuality:(NSUInteger)quality
  219. {
  220. FBScreenshotQuality = quality;
  221. }
  222. + (NSTimeInterval)waitForIdleTimeout
  223. {
  224. return FBWaitForIdleTimeout;
  225. }
  226. + (void)setWaitForIdleTimeout:(NSTimeInterval)timeout
  227. {
  228. FBWaitForIdleTimeout = timeout;
  229. }
  230. + (NSTimeInterval)animationCoolOffTimeout
  231. {
  232. return FBAnimationCoolOffTimeout;
  233. }
  234. + (void)setAnimationCoolOffTimeout:(NSTimeInterval)timeout
  235. {
  236. FBAnimationCoolOffTimeout = timeout;
  237. }
  238. // Works for Simulator and Real devices
  239. + (void)configureDefaultKeyboardPreferences
  240. {
  241. void *handle = dlopen(controllerPrefBundlePath, RTLD_LAZY);
  242. Class controllerClass = NSClassFromString(controllerClassName);
  243. TIPreferencesController *controller = [controllerClass sharedPreferencesController];
  244. // Auto-Correction in Keyboards
  245. // 'setAutocorrectionEnabled' Was in TextInput.framework/TIKeyboardState.h over iOS 10.3
  246. if ([controller respondsToSelector:@selector(setAutocorrectionEnabled:)]) {
  247. // Under iOS 10.2
  248. controller.autocorrectionEnabled = NO;
  249. } else if ([controller respondsToSelector:@selector(setValue:forPreferenceKey:)]) {
  250. // Over iOS 10.3
  251. [controller setValue:@NO forPreferenceKey:FBKeyboardAutocorrectionKey];
  252. }
  253. // Predictive in Keyboards
  254. if ([controller respondsToSelector:@selector(setPredictionEnabled:)]) {
  255. controller.predictionEnabled = NO;
  256. } else if ([controller respondsToSelector:@selector(setValue:forPreferenceKey:)]) {
  257. [controller setValue:@NO forPreferenceKey:FBKeyboardPredictionKey];
  258. }
  259. // To dismiss keyboard tutorial on iOS 11+ (iPad)
  260. if ([controller respondsToSelector:@selector(setValue:forPreferenceKey:)]) {
  261. [controller setValue:@YES forPreferenceKey:@"DidShowGestureKeyboardIntroduction"];
  262. if (isSDKVersionGreaterThanOrEqualTo(@"13.0")) {
  263. [controller setValue:@YES forPreferenceKey:@"DidShowContinuousPathIntroduction"];
  264. }
  265. [controller synchronizePreferences];
  266. }
  267. dlclose(handle);
  268. }
  269. + (void)forceSimulatorSoftwareKeyboardPresence
  270. {
  271. #if TARGET_OS_SIMULATOR
  272. // Force toggle software keyboard on.
  273. // This can avoid 'Keyboard is not present' error which can happen
  274. // when send_keys are called by client
  275. [[UIKeyboardImpl sharedInstance] setAutomaticMinimizationEnabled:NO];
  276. if ([(NSObject *)[UIKeyboardImpl sharedInstance]
  277. respondsToSelector:@selector(setSoftwareKeyboardShownByTouch:)]) {
  278. // Xcode 13 no longer has this method
  279. [[UIKeyboardImpl sharedInstance] setSoftwareKeyboardShownByTouch:YES];
  280. }
  281. #endif
  282. }
  283. + (FBConfigurationKeyboardPreference)keyboardAutocorrection
  284. {
  285. return [self keyboardsPreference:FBKeyboardAutocorrectionKey];
  286. }
  287. + (void)setKeyboardAutocorrection:(BOOL)isEnabled
  288. {
  289. [self configureKeyboardsPreference:isEnabled forPreferenceKey:FBKeyboardAutocorrectionKey];
  290. }
  291. + (FBConfigurationKeyboardPreference)keyboardPrediction
  292. {
  293. return [self keyboardsPreference:FBKeyboardPredictionKey];
  294. }
  295. + (void)setKeyboardPrediction:(BOOL)isEnabled
  296. {
  297. [self configureKeyboardsPreference:isEnabled forPreferenceKey:FBKeyboardPredictionKey];
  298. }
  299. + (void)setSnapshotMaxDepth:(int)maxDepth
  300. {
  301. FBSetCustomParameterForElementSnapshot(FBSnapshotMaxDepthKey, @(maxDepth));
  302. }
  303. + (int)snapshotMaxDepth
  304. {
  305. return [FBGetCustomParameterForElementSnapshot(FBSnapshotMaxDepthKey) intValue];
  306. }
  307. + (void)setShouldRespectSystemAlerts:(BOOL)value
  308. {
  309. FBShouldRespectSystemAlerts = value;
  310. }
  311. + (BOOL)shouldRespectSystemAlerts
  312. {
  313. return FBShouldRespectSystemAlerts;
  314. }
  315. + (void)setUseFirstMatch:(BOOL)enabled
  316. {
  317. FBShouldUseFirstMatch = enabled;
  318. }
  319. + (BOOL)useFirstMatch
  320. {
  321. return FBShouldUseFirstMatch;
  322. }
  323. + (void)setBoundElementsByIndex:(BOOL)enabled
  324. {
  325. FBShouldBoundElementsByIndex = enabled;
  326. }
  327. + (BOOL)boundElementsByIndex
  328. {
  329. return FBShouldBoundElementsByIndex;
  330. }
  331. + (void)setIncludeNonModalElements:(BOOL)isEnabled
  332. {
  333. FBIncludeNonModalElements = isEnabled;
  334. }
  335. + (BOOL)includeNonModalElements
  336. {
  337. return FBIncludeNonModalElements;
  338. }
  339. + (void)setAcceptAlertButtonSelector:(NSString *)classChainSelector
  340. {
  341. FBAcceptAlertButtonSelector = classChainSelector;
  342. }
  343. + (NSString *)acceptAlertButtonSelector
  344. {
  345. return FBAcceptAlertButtonSelector;
  346. }
  347. + (void)setDismissAlertButtonSelector:(NSString *)classChainSelector
  348. {
  349. FBDismissAlertButtonSelector = classChainSelector;
  350. }
  351. + (NSString *)dismissAlertButtonSelector
  352. {
  353. return FBDismissAlertButtonSelector;
  354. }
  355. + (void)setAutoClickAlertSelector:(NSString *)classChainSelector
  356. {
  357. FBAutoClickAlertSelector = classChainSelector;
  358. }
  359. + (NSString *)autoClickAlertSelector
  360. {
  361. return FBAutoClickAlertSelector;
  362. }
  363. + (void)setUseClearTextShortcut:(BOOL)enabled
  364. {
  365. FBUseClearTextShortcut = enabled;
  366. }
  367. + (BOOL)useClearTextShortcut
  368. {
  369. return FBUseClearTextShortcut;
  370. }
  371. + (BOOL)limitXpathContextScope
  372. {
  373. return FBLimitXpathContextScope;
  374. }
  375. + (void)setLimitXpathContextScope:(BOOL)enabled
  376. {
  377. FBLimitXpathContextScope = enabled;
  378. }
  379. #if !TARGET_OS_TV
  380. + (BOOL)setScreenshotOrientation:(NSString *)orientation error:(NSError **)error
  381. {
  382. // Only UIInterfaceOrientationUnknown is over iOS 8. Others are over iOS 2.
  383. // https://developer.apple.com/documentation/uikit/uiinterfaceorientation/uiinterfaceorientationunknown
  384. if ([orientation.lowercaseString isEqualToString:@"portrait"]) {
  385. FBScreenshotOrientation = UIInterfaceOrientationPortrait;
  386. } else if ([orientation.lowercaseString isEqualToString:@"portraitupsidedown"]) {
  387. FBScreenshotOrientation = UIInterfaceOrientationPortraitUpsideDown;
  388. } else if ([orientation.lowercaseString isEqualToString:@"landscaperight"]) {
  389. FBScreenshotOrientation = UIInterfaceOrientationLandscapeRight;
  390. } else if ([orientation.lowercaseString isEqualToString:@"landscapeleft"]) {
  391. FBScreenshotOrientation = UIInterfaceOrientationLandscapeLeft;
  392. } else if ([orientation.lowercaseString isEqualToString:@"auto"]) {
  393. FBScreenshotOrientation = UIInterfaceOrientationUnknown;
  394. } else {
  395. return [[FBErrorBuilder.builder withDescriptionFormat:
  396. @"The orientation value '%@' is not known. Only the following orientation values are supported: " \
  397. "'auto', 'portrait', 'portraitUpsideDown', 'landscapeRight' and 'landscapeLeft'", orientation]
  398. buildError:error];
  399. }
  400. return YES;
  401. }
  402. + (NSInteger)screenshotOrientation
  403. {
  404. return FBScreenshotOrientation;
  405. }
  406. + (NSString *)humanReadableScreenshotOrientation
  407. {
  408. switch (FBScreenshotOrientation) {
  409. case UIInterfaceOrientationPortrait:
  410. return @"portrait";
  411. case UIInterfaceOrientationPortraitUpsideDown:
  412. return @"portraitUpsideDown";
  413. case UIInterfaceOrientationLandscapeRight:
  414. return @"landscapeRight";
  415. case UIInterfaceOrientationLandscapeLeft:
  416. return @"landscapeLeft";
  417. case UIInterfaceOrientationUnknown:
  418. return @"auto";
  419. }
  420. }
  421. #endif
  422. + (void)resetSessionSettings
  423. {
  424. FBShouldTerminateApp = YES;
  425. FBShouldUseCompactResponses = YES;
  426. FBElementResponseAttributes = @"type,label";
  427. FBMaxTypingFrequency = @([self defaultTypingFrequency]);
  428. FBScreenshotQuality = 3;
  429. FBShouldUseFirstMatch = NO;
  430. FBShouldBoundElementsByIndex = NO;
  431. // This is diabled by default because enabling it prevents the accessbility snapshot to be taken
  432. // (it always errors with kxIllegalArgument error)
  433. FBIncludeNonModalElements = NO;
  434. FBAcceptAlertButtonSelector = @"";
  435. FBDismissAlertButtonSelector = @"";
  436. FBAutoClickAlertSelector = @"";
  437. FBWaitForIdleTimeout = 10.;
  438. FBAnimationCoolOffTimeout = 2.;
  439. // 50 should be enough for the majority of the cases. The performance is acceptable for values up to 100.
  440. FBSetCustomParameterForElementSnapshot(FBSnapshotMaxDepthKey, @50);
  441. FBUseClearTextShortcut = YES;
  442. FBLimitXpathContextScope = YES;
  443. #if !TARGET_OS_TV
  444. FBScreenshotOrientation = UIInterfaceOrientationUnknown;
  445. #endif
  446. }
  447. #pragma mark Private
  448. + (FBConfigurationKeyboardPreference)keyboardsPreference:(nonnull NSString *)key
  449. {
  450. Class controllerClass = NSClassFromString(controllerClassName);
  451. TIPreferencesController *controller = [controllerClass sharedPreferencesController];
  452. if ([key isEqualToString:FBKeyboardAutocorrectionKey]) {
  453. if ([controller respondsToSelector:@selector(boolForPreferenceKey:)]) {
  454. return [controller boolForPreferenceKey:FBKeyboardAutocorrectionKey]
  455. ? FBConfigurationKeyboardPreferenceEnabled
  456. : FBConfigurationKeyboardPreferenceDisabled;
  457. } else {
  458. [FBLogger log:@"Updating keyboard autocorrection preference is not supported"];
  459. return FBConfigurationKeyboardPreferenceNotSupported;
  460. }
  461. } else if ([key isEqualToString:FBKeyboardPredictionKey]) {
  462. if ([controller respondsToSelector:@selector(boolForPreferenceKey:)]) {
  463. return [controller boolForPreferenceKey:FBKeyboardPredictionKey]
  464. ? FBConfigurationKeyboardPreferenceEnabled
  465. : FBConfigurationKeyboardPreferenceDisabled;
  466. } else {
  467. [FBLogger log:@"Updating keyboard prediction preference is not supported"];
  468. return FBConfigurationKeyboardPreferenceNotSupported;
  469. }
  470. }
  471. @throw [[FBErrorBuilder.builder withDescriptionFormat:@"No available keyboardsPreferenceKey: '%@'", key] build];
  472. }
  473. + (void)configureKeyboardsPreference:(BOOL)enable forPreferenceKey:(nonnull NSString *)key
  474. {
  475. void *handle = dlopen(controllerPrefBundlePath, RTLD_LAZY);
  476. Class controllerClass = NSClassFromString(controllerClassName);
  477. TIPreferencesController *controller = [controllerClass sharedPreferencesController];
  478. if ([key isEqualToString:FBKeyboardAutocorrectionKey]) {
  479. // Auto-Correction in Keyboards
  480. if ([controller respondsToSelector:@selector(setAutocorrectionEnabled:)]) {
  481. controller.autocorrectionEnabled = enable;
  482. } else {
  483. [controller setValue:@(enable) forPreferenceKey:FBKeyboardAutocorrectionKey];
  484. }
  485. } else if ([key isEqualToString:FBKeyboardPredictionKey]) {
  486. // Predictive in Keyboards
  487. if ([controller respondsToSelector:@selector(setPredictionEnabled:)]) {
  488. controller.predictionEnabled = enable;
  489. } else {
  490. [controller setValue:@(enable) forPreferenceKey:FBKeyboardPredictionKey];
  491. }
  492. }
  493. [controller synchronizePreferences];
  494. dlclose(handle);
  495. }
  496. + (NSString*)valueFromArguments: (NSArray<NSString *> *)arguments forKey: (NSString*)key
  497. {
  498. NSUInteger index = [arguments indexOfObject:key];
  499. if (index == NSNotFound || index == arguments.count - 1) {
  500. return nil;
  501. }
  502. return arguments[index + 1];
  503. }
  504. + (NSUInteger)mjpegServerPortFromArguments
  505. {
  506. NSString *portNumberString = [self valueFromArguments: NSProcessInfo.processInfo.arguments
  507. forKey: @"--mjpeg-server-port"];
  508. NSUInteger port = (NSUInteger)[portNumberString integerValue];
  509. if (port == 0) {
  510. return NSNotFound;
  511. }
  512. return port;
  513. }
  514. + (NSRange)bindingPortRangeFromArguments
  515. {
  516. NSString *portNumberString = [self valueFromArguments:NSProcessInfo.processInfo.arguments
  517. forKey: @"--port"];
  518. NSUInteger port = (NSUInteger)[portNumberString integerValue];
  519. if (port == 0) {
  520. return NSMakeRange(NSNotFound, 0);
  521. }
  522. return NSMakeRange(port, 1);
  523. }
  524. + (void)setReduceMotionEnabled:(BOOL)isEnabled
  525. {
  526. Class settingsClass = NSClassFromString(axSettingsClassName);
  527. AXSettings *settings = [settingsClass sharedInstance];
  528. // Below does not work on real devices because of iOS security model
  529. // (lldb) po settings.reduceMotionEnabled = isEnabled
  530. // 2019-08-21 22:58:19.776165+0900 WebDriverAgentRunner-Runner[322:13361] [User Defaults] Couldn't write value for key ReduceMotionEnabled in CFPrefsPlistSource<0x28111a700> (Domain: com.apple.Accessibility, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): setting preferences outside an application's container requires user-preference-write or file-write-data sandbox access
  531. if ([settings respondsToSelector:@selector(setReduceMotionEnabled:)]) {
  532. [settings setReduceMotionEnabled:isEnabled];
  533. }
  534. }
  535. + (BOOL)reduceMotionEnabled
  536. {
  537. Class settingsClass = NSClassFromString(axSettingsClassName);
  538. AXSettings *settings = [settingsClass sharedInstance];
  539. if ([settings respondsToSelector:@selector(reduceMotionEnabled)]) {
  540. return settings.reduceMotionEnabled;
  541. }
  542. return NO;
  543. }
  544. + (void)setIncludeHittableInPageSource:(BOOL)enabled
  545. {
  546. FBShouldIncludeHittableInPageSource = enabled;
  547. }
  548. + (BOOL)includeHittableInPageSource
  549. {
  550. return FBShouldIncludeHittableInPageSource;
  551. }
  552. + (void)setIncludeNativeFrameInPageSource:(BOOL)enabled
  553. {
  554. FBShouldIncludeNativeFrameInPageSource = enabled;
  555. }
  556. + (BOOL)includeNativeFrameInPageSource
  557. {
  558. return FBShouldIncludeNativeFrameInPageSource;
  559. }
  560. + (void)setIncludeMinMaxValueInPageSource:(BOOL)enabled
  561. {
  562. FBShouldIncludeMinMaxValueInPageSource = enabled;
  563. }
  564. + (BOOL)includeMinMaxValueInPageSource
  565. {
  566. return FBShouldIncludeMinMaxValueInPageSource;
  567. }
  568. @end