appium-webdriveragent 5.14.0 → 5.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## [5.15.2](https://github.com/appium/WebDriverAgent/compare/v5.15.1...v5.15.2) (2023-11-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fix run test ci ([#814](https://github.com/appium/WebDriverAgent/issues/814)) ([014d04d](https://github.com/appium/WebDriverAgent/commit/014d04df956e47fef67938b089511e80d344f007))
7
+
8
+
9
+ ### Miscellaneous Chores
10
+
11
+ * a dummy commit to check a package release ([08388fd](https://github.com/appium/WebDriverAgent/commit/08388fd602ee9d588a8780e8d141d748813782ed))
12
+
13
+ ## [5.15.1](https://github.com/appium/WebDriverAgent/compare/v5.15.0...v5.15.1) (2023-11-16)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * Content-Type of the MJPEG server ([b4704da](https://github.com/appium/WebDriverAgent/commit/b4704dafc4567e1f0dc8675facfc48a195aae4bf))
19
+
20
+
21
+ ### Code Refactoring
22
+
23
+ * Optimize screenshots preprocessing ([#812](https://github.com/appium/WebDriverAgent/issues/812)) ([0b41757](https://github.com/appium/WebDriverAgent/commit/0b41757c0d21004afab32860b4e510d4bc426018))
24
+
25
+ ## [5.15.0](https://github.com/appium/WebDriverAgent/compare/v5.14.0...v5.15.0) (2023-11-16)
26
+
27
+
28
+ ### Features
29
+
30
+ * Add element attributes to the performAccessibilityAudit output ([#808](https://github.com/appium/WebDriverAgent/issues/808)) ([0d7e4a6](https://github.com/appium/WebDriverAgent/commit/0d7e4a697adb7355279583eaa05118f396056e6f))
31
+
1
32
  ## [5.14.0](https://github.com/appium/WebDriverAgent/compare/v5.13.3...v5.14.0) (2023-11-10)
2
33
 
3
34
 
@@ -38,11 +38,11 @@
38
38
  315A15072518CC2800A3A064 /* TouchSpotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 315A15062518CC2800A3A064 /* TouchSpotView.m */; };
39
39
  315A150A2518D6F400A3A064 /* TouchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 315A15092518D6F400A3A064 /* TouchViewController.m */; };
40
40
  6385F4A7220A40760095BBDB /* XCUIApplicationProcessDelay.m in Sources */ = {isa = PBXBuildFile; fileRef = 6385F4A5220A40760095BBDB /* XCUIApplicationProcessDelay.m */; };
41
- 63CCF91221ECE4C700E94ABD /* FBImageIOScaler.h in Headers */ = {isa = PBXBuildFile; fileRef = 63CCF91021ECE4C700E94ABD /* FBImageIOScaler.h */; };
42
- 63CCF91321ECE4C700E94ABD /* FBImageIOScaler.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CCF91121ECE4C700E94ABD /* FBImageIOScaler.m */; };
43
- 63FD950221F9D06100A3E356 /* FBImageIOScalerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageIOScalerTests.m */; };
44
- 63FD950321F9D06100A3E356 /* FBImageIOScalerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageIOScalerTests.m */; };
45
- 63FD950421F9D06200A3E356 /* FBImageIOScalerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageIOScalerTests.m */; };
41
+ 63CCF91221ECE4C700E94ABD /* FBImageProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */; };
42
+ 63CCF91321ECE4C700E94ABD /* FBImageProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */; };
43
+ 63FD950221F9D06100A3E356 /* FBImageProcessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageProcessorTests.m */; };
44
+ 63FD950321F9D06100A3E356 /* FBImageProcessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageProcessorTests.m */; };
45
+ 63FD950421F9D06200A3E356 /* FBImageProcessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageProcessorTests.m */; };
46
46
  641EE3452240C1C800173FCB /* UITestingUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB7FD1CAEE048008C271F /* UITestingUITests.m */; };
47
47
  641EE5D72240C5CA00173FCB /* FBScreenshotCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB75F1CAEDF0C008C271F /* FBScreenshotCommands.m */; };
48
48
  641EE5D92240C5CA00173FCB /* XCUIElement+FBPickerWheel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7136A4781E8918E60024FC3D /* XCUIElement+FBPickerWheel.m */; };
@@ -80,7 +80,7 @@
80
80
  641EE5FE2240C5CA00173FCB /* XCUIElement+FBWebDriverAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE376481D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m */; };
81
81
  641EE5FF2240C5CA00173FCB /* XCUIElement+FBForceTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8DDD7C20C5733B004D4925 /* XCUIElement+FBForceTouch.m */; };
82
82
  641EE6002240C5CA00173FCB /* FBTouchActionCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 71241D7A1FAE3D2500B9559F /* FBTouchActionCommands.m */; };
83
- 641EE6012240C5CA00173FCB /* FBImageIOScaler.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CCF91121ECE4C700E94ABD /* FBImageIOScaler.m */; };
83
+ 641EE6012240C5CA00173FCB /* FBImageProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */; };
84
84
  641EE6022240C5CA00173FCB /* FBTouchIDCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB7631CAEDF0C008C271F /* FBTouchIDCommands.m */; };
85
85
  641EE6032240C5CA00173FCB /* FBDebugCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB7551CAEDF0C008C271F /* FBDebugCommands.m */; };
86
86
  641EE6042240C5CA00173FCB /* NSString+FBXMLSafeString.m in Sources */ = {isa = PBXBuildFile; fileRef = 716E0BCD1E917E810087A825 /* NSString+FBXMLSafeString.m */; };
@@ -222,7 +222,7 @@
222
222
  641EE6A32240C5CA00173FCB /* XCUIApplication+FBTouchAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 71BD20711F86116100B36EC2 /* XCUIApplication+FBTouchAction.h */; };
223
223
  641EE6A42240C5CA00173FCB /* FBCommandHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7751CAEDF0C008C271F /* FBCommandHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
224
224
  641EE6A52240C5CA00173FCB /* FBSessionCommands.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7601CAEDF0C008C271F /* FBSessionCommands.h */; };
225
- 641EE6A62240C5CA00173FCB /* FBImageIOScaler.h in Headers */ = {isa = PBXBuildFile; fileRef = 63CCF91021ECE4C700E94ABD /* FBImageIOScaler.h */; };
225
+ 641EE6A62240C5CA00173FCB /* FBImageProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */; };
226
226
  641EE6A72240C5CA00173FCB /* FBSession-Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7891CAEDF0C008C271F /* FBSession-Private.h */; };
227
227
  641EE6A82240C5CA00173FCB /* NSString+FBXMLSafeString.h in Headers */ = {isa = PBXBuildFile; fileRef = 716E0BCC1E917E810087A825 /* NSString+FBXMLSafeString.h */; };
228
228
  641EE6A92240C5CA00173FCB /* FBCommandStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7761CAEDF0C008C271F /* FBCommandStatus.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -918,11 +918,11 @@
918
918
  315A15082518D6F400A3A064 /* TouchViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchViewController.h; sourceTree = "<group>"; };
919
919
  315A15092518D6F400A3A064 /* TouchViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchViewController.m; sourceTree = "<group>"; };
920
920
  44757A831D42CE8300ECF35E /* XCUIDeviceRotationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XCUIDeviceRotationTests.m; sourceTree = "<group>"; };
921
- 631B523421F6174300625362 /* FBImageIOScalerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageIOScalerTests.m; sourceTree = "<group>"; };
921
+ 631B523421F6174300625362 /* FBImageProcessorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageProcessorTests.m; sourceTree = "<group>"; };
922
922
  633E904A220DEE7F007CADF9 /* XCUIApplicationProcessDelay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCUIApplicationProcessDelay.h; sourceTree = "<group>"; };
923
923
  6385F4A5220A40760095BBDB /* XCUIApplicationProcessDelay.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XCUIApplicationProcessDelay.m; sourceTree = "<group>"; };
924
- 63CCF91021ECE4C700E94ABD /* FBImageIOScaler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBImageIOScaler.h; sourceTree = "<group>"; };
925
- 63CCF91121ECE4C700E94ABD /* FBImageIOScaler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageIOScaler.m; sourceTree = "<group>"; };
924
+ 63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBImageProcessor.h; sourceTree = "<group>"; };
925
+ 63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageProcessor.m; sourceTree = "<group>"; };
926
926
  641EE2DA2240BBE300173FCB /* WebDriverAgentRunner_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebDriverAgentRunner_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
927
927
  641EE6F82240C5CA00173FCB /* WebDriverAgentLib_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WebDriverAgentLib_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
928
928
  641EE7042240CDCF00173FCB /* XCUIElement+FBTVFocuse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCUIElement+FBTVFocuse.h"; sourceTree = "<group>"; };
@@ -1890,8 +1890,8 @@
1890
1890
  EE3A18611CDE618F00DE4205 /* FBErrorBuilder.m */,
1891
1891
  EE6A89381D0B38640083E92B /* FBFailureProofTestCase.h */,
1892
1892
  EE6A89391D0B38640083E92B /* FBFailureProofTestCase.m */,
1893
- 63CCF91021ECE4C700E94ABD /* FBImageIOScaler.h */,
1894
- 63CCF91121ECE4C700E94ABD /* FBImageIOScaler.m */,
1893
+ 63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */,
1894
+ 63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */,
1895
1895
  7150348521A6DAD600A0F4BA /* FBImageUtils.h */,
1896
1896
  7150348621A6DAD600A0F4BA /* FBImageUtils.m */,
1897
1897
  EE9B76A31CF7A43900275851 /* FBLogger.h */,
@@ -2004,7 +2004,7 @@
2004
2004
  71E504941DF59BAD0020C32A /* XCUIElementAttributesTests.m */,
2005
2005
  EEBBD48D1D4785FC00656A81 /* XCUIElementFBFindTests.m */,
2006
2006
  EE1E06E11D181CC9007CF043 /* XCUIElementHelperIntegrationTests.m */,
2007
- 631B523421F6174300625362 /* FBImageIOScalerTests.m */,
2007
+ 631B523421F6174300625362 /* FBImageProcessorTests.m */,
2008
2008
  644D9CCD230E1F1A00C90459 /* FBConfigurationTests.m */,
2009
2009
  );
2010
2010
  path = IntegrationTests;
@@ -2381,7 +2381,7 @@
2381
2381
  641EE6A42240C5CA00173FCB /* FBCommandHandler.h in Headers */,
2382
2382
  641EE6A52240C5CA00173FCB /* FBSessionCommands.h in Headers */,
2383
2383
  641EE70C2240CE2D00173FCB /* FBTVNavigationTracker.h in Headers */,
2384
- 641EE6A62240C5CA00173FCB /* FBImageIOScaler.h in Headers */,
2384
+ 641EE6A62240C5CA00173FCB /* FBImageProcessor.h in Headers */,
2385
2385
  641EE6A72240C5CA00173FCB /* FBSession-Private.h in Headers */,
2386
2386
  641EE6A82240C5CA00173FCB /* NSString+FBXMLSafeString.h in Headers */,
2387
2387
  64E3502F2AC0B6FE005F3ACB /* NSDictionary+FBUtf8SafeDictionary.h in Headers */,
@@ -2622,7 +2622,7 @@
2622
2622
  EE158AC81CBD456F00A3E3F0 /* FBSessionCommands.h in Headers */,
2623
2623
  71C8E55125399A6B008572C1 /* XCUIApplication+FBQuiescence.h in Headers */,
2624
2624
  641EE70B2240CE2D00173FCB /* FBTVNavigationTracker.h in Headers */,
2625
- 63CCF91221ECE4C700E94ABD /* FBImageIOScaler.h in Headers */,
2625
+ 63CCF91221ECE4C700E94ABD /* FBImageProcessor.h in Headers */,
2626
2626
  EE158AE31CBD456F00A3E3F0 /* FBSession-Private.h in Headers */,
2627
2627
  716E0BCE1E917E810087A825 /* NSString+FBXMLSafeString.h in Headers */,
2628
2628
  EE158ACF1CBD456F00A3E3F0 /* FBCommandStatus.h in Headers */,
@@ -3122,7 +3122,7 @@
3122
3122
  641EE6002240C5CA00173FCB /* FBTouchActionCommands.m in Sources */,
3123
3123
  719DCF182601EAFB000E765F /* FBNotificationsHelper.m in Sources */,
3124
3124
  714EAA102673FDFE005C5B47 /* FBCapabilities.m in Sources */,
3125
- 641EE6012240C5CA00173FCB /* FBImageIOScaler.m in Sources */,
3125
+ 641EE6012240C5CA00173FCB /* FBImageProcessor.m in Sources */,
3126
3126
  641EE6022240C5CA00173FCB /* FBTouchIDCommands.m in Sources */,
3127
3127
  641EE6032240C5CA00173FCB /* FBDebugCommands.m in Sources */,
3128
3128
  641EE6042240C5CA00173FCB /* NSString+FBXMLSafeString.m in Sources */,
@@ -3236,7 +3236,7 @@
3236
3236
  EEE3764A1D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m in Sources */,
3237
3237
  EE8DDD7E20C5733C004D4925 /* XCUIElement+FBForceTouch.m in Sources */,
3238
3238
  71241D7C1FAE3D2500B9559F /* FBTouchActionCommands.m in Sources */,
3239
- 63CCF91321ECE4C700E94ABD /* FBImageIOScaler.m in Sources */,
3239
+ 63CCF91321ECE4C700E94ABD /* FBImageProcessor.m in Sources */,
3240
3240
  EE158ACB1CBD456F00A3E3F0 /* FBTouchIDCommands.m in Sources */,
3241
3241
  71F5BE51252F14EB00EE9EBA /* FBExceptions.m in Sources */,
3242
3242
  EE158ABD1CBD456F00A3E3F0 /* FBDebugCommands.m in Sources */,
@@ -3307,7 +3307,7 @@
3307
3307
  files = (
3308
3308
  71241D801FAF087500B9559F /* FBW3CMultiTouchActionsIntegrationTests.m in Sources */,
3309
3309
  71241D7E1FAF084E00B9559F /* FBW3CTouchActionsIntegrationTests.m in Sources */,
3310
- 63FD950221F9D06100A3E356 /* FBImageIOScalerTests.m in Sources */,
3310
+ 63FD950221F9D06100A3E356 /* FBImageProcessorTests.m in Sources */,
3311
3311
  719CD8FF2126C90200C7D0C2 /* FBAutoAlertsHandlerTests.m in Sources */,
3312
3312
  EE2202131ECC612200A29571 /* FBIntegrationTestCase.m in Sources */,
3313
3313
  71BD20781F869E0F00B36EC2 /* FBAppiumTouchActionsIntegrationTests.m in Sources */,
@@ -3324,7 +3324,7 @@
3324
3324
  buildActionMask = 2147483647;
3325
3325
  files = (
3326
3326
  EE5095E51EBCC9090028E2FE /* FBTypingTest.m in Sources */,
3327
- 63FD950321F9D06100A3E356 /* FBImageIOScalerTests.m in Sources */,
3327
+ 63FD950321F9D06100A3E356 /* FBImageProcessorTests.m in Sources */,
3328
3328
  EE5095EB1EBCC9090028E2FE /* XCElementSnapshotHitPointTests.m in Sources */,
3329
3329
  EE5095EC1EBCC9090028E2FE /* XCUIApplicationHelperTests.m in Sources */,
3330
3330
  7136C0F9243A182400921C76 /* FBW3CTypeActionsTests.m in Sources */,
@@ -3397,7 +3397,7 @@
3397
3397
  7119E1EC1E891F8600D0B125 /* FBPickerWheelSelectTests.m in Sources */,
3398
3398
  71ACF5B8242F2FDC00F0AAD4 /* FBSafariAlertTests.m in Sources */,
3399
3399
  EE1E06DA1D1808C2007CF043 /* FBIntegrationTestCase.m in Sources */,
3400
- 63FD950421F9D06200A3E356 /* FBImageIOScalerTests.m in Sources */,
3400
+ 63FD950421F9D06200A3E356 /* FBImageProcessorTests.m in Sources */,
3401
3401
  EE05BAFA1D13003C00A3EB00 /* FBKeyboardTests.m in Sources */,
3402
3402
  EE55B3271D1D54CF003AAAEC /* FBScrollingTests.m in Sources */,
3403
3403
  EE6A89371D0B35920083E92B /* FBFailureProofTestCaseTests.m in Sources */,
@@ -369,12 +369,19 @@ NSDictionary<NSNumber *, NSString *> *auditTypeValuesToNames(void) {
369
369
  if (nil != auditTypeValue) {
370
370
  auditType = valuesToNamesMap[auditTypeValue] ?: [auditTypeValue stringValue];
371
371
  }
372
+
373
+ id extractedElement = extractIssueProperty(issue, @"element");
374
+
375
+ id<FBXCElementSnapshot> elementSnapshot = [extractedElement fb_takeSnapshot];
376
+ NSDictionary *elementAttributes = elementSnapshot ? [self.class dictionaryForElement:elementSnapshot recursive:NO] : @{};
377
+
372
378
  [resultArray addObject:@{
373
379
  @"detailedDescription": extractIssueProperty(issue, @"detailedDescription") ?: @"",
374
380
  @"compactDescription": extractIssueProperty(issue, @"compactDescription") ?: @"",
375
381
  @"auditType": auditType,
376
- @"element": [extractIssueProperty(issue, @"element") description] ?: @"",
377
- @"elementDescription": [extractIssueProperty(issue, @"element") debugDescription] ?: @"",
382
+ @"element": [extractedElement description] ?: @"",
383
+ @"elementDescription": [extractedElement debugDescription] ?: @"",
384
+ @"elementAttributes": elementAttributes ?: @{},
378
385
  }];
379
386
  return YES;
380
387
  };
@@ -280,6 +280,7 @@
280
280
  FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY: @([FBConfiguration mjpegServerScreenshotQuality]),
281
281
  FB_SETTING_MJPEG_SERVER_FRAMERATE: @([FBConfiguration mjpegServerFramerate]),
282
282
  FB_SETTING_MJPEG_SCALING_FACTOR: @([FBConfiguration mjpegScalingFactor]),
283
+ FB_SETTING_MJPEG_FIX_ORIENTATION: @([FBConfiguration mjpegShouldFixOrientation]),
283
284
  FB_SETTING_SCREENSHOT_QUALITY: @([FBConfiguration screenshotQuality]),
284
285
  FB_SETTING_KEYBOARD_AUTOCORRECTION: @([FBConfiguration keyboardAutocorrection]),
285
286
  FB_SETTING_KEYBOARD_PREDICTION: @([FBConfiguration keyboardPrediction]),
@@ -327,6 +328,9 @@
327
328
  if (nil != [settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR]) {
328
329
  [FBConfiguration setMjpegScalingFactor:[[settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR] unsignedIntegerValue]];
329
330
  }
331
+ if (nil != [settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION]) {
332
+ [FBConfiguration setMjpegShouldFixOrientation:[[settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION] boolValue]];
333
+ }
330
334
  if (nil != [settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION]) {
331
335
  [FBConfiguration setKeyboardAutocorrection:[[settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION] boolValue]];
332
336
  }
@@ -84,6 +84,16 @@ extern NSString *const FBSnapshotMaxDepthKey;
84
84
  + (NSUInteger)mjpegServerScreenshotQuality;
85
85
  + (void)setMjpegServerScreenshotQuality:(NSUInteger)quality;
86
86
 
87
+ /**
88
+ Whether to apply orientation fixes to the streamed JPEG images.
89
+ This is an expensive operation and it is disabled by default, so screenshots
90
+ are returned in portrait, but their actual orientation value could still be found in the EXIF
91
+ metadata.
92
+ ! Enablement of this setting may lead to WDA process termination because of an excessive CPU usage.
93
+ */
94
+ + (BOOL)mjpegShouldFixOrientation;
95
+ + (void)setMjpegShouldFixOrientation:(BOOL)enabled;
96
+
87
97
  /**
88
98
  The framerate at which the background screenshots broadcaster should broadcast
89
99
  screenshots in range 1..60. The default value is 10 (Frames Per Second).
@@ -93,7 +103,7 @@ extern NSString *const FBSnapshotMaxDepthKey;
93
103
  + (void)setMjpegServerFramerate:(NSUInteger)framerate;
94
104
 
95
105
  /**
96
- The quality of display screenshots. The higher quality you set is the bigger screenshot size is.
106
+ The quality of display screenshots. The higher quality you set is the bigger screenshot size is.
97
107
  The highest quality value is 0 (lossless PNG) or 3 (lossless HEIC). The lowest quality is 2 (highly compressed JPEG).
98
108
  The default quality value is 3 (lossless HEIC).
99
109
  See https://developer.apple.com/documentation/xctest/xctimagequality?language=objc
@@ -112,7 +122,10 @@ extern NSString *const FBSnapshotMaxDepthKey;
112
122
  + (NSInteger)mjpegServerPort;
113
123
 
114
124
  /**
115
- The scaling factor for frames of the mjpeg stream (Default values is 100 and does not perform scaling).
125
+ The scaling factor for frames of the mjpeg stream. The default (and maximum) value is 100,
126
+ which does not perform any scaling. The minimum value must be greater than zero.
127
+ ! Setting this to a value less than 100, especially together with orientation fixing enabled
128
+ ! may lead to WDA process termination because of an excessive CPU usage.
116
129
  */
117
130
  + (NSUInteger)mjpegScalingFactor;
118
131
  + (void)setMjpegScalingFactor:(NSUInteger)scalingFactor;
@@ -33,6 +33,7 @@ static BOOL FBShouldUseTestManagerForVisibilityDetection = NO;
33
33
  static BOOL FBShouldUseSingletonTestManager = YES;
34
34
 
35
35
  static NSUInteger FBMjpegScalingFactor = 100;
36
+ static BOOL FBMjpegShouldFixOrientation = NO;
36
37
  static NSUInteger FBMjpegServerScreenshotQuality = 25;
37
38
  static NSUInteger FBMjpegServerFramerate = 10;
38
39
 
@@ -143,6 +144,15 @@ static UIInterfaceOrientation FBScreenshotOrientation;
143
144
  FBMjpegScalingFactor = scalingFactor;
144
145
  }
145
146
 
147
+ + (BOOL)mjpegShouldFixOrientation
148
+ {
149
+ return FBMjpegShouldFixOrientation;
150
+ }
151
+
152
+ + (void)setMjpegShouldFixOrientation:(BOOL)enabled {
153
+ FBMjpegShouldFixOrientation = enabled;
154
+ }
155
+
146
156
  + (BOOL)verboseLoggingEnabled
147
157
  {
148
158
  return [NSProcessInfo.processInfo.environment[@"VERBOSE_LOGGING"] boolValue];
@@ -20,7 +20,7 @@ extern const CGFloat FBMaxScalingFactor;
20
20
  extern const CGFloat FBMinCompressionQuality;
21
21
  extern const CGFloat FBMaxCompressionQuality;
22
22
 
23
- @interface FBImageIOScaler : NSObject
23
+ @interface FBImageProcessor : NSObject
24
24
 
25
25
  /**
26
26
  Puts the passed image on the queue and dispatches a scaling operation. If there is already a image on the
@@ -29,34 +29,27 @@ extern const CGFloat FBMaxCompressionQuality;
29
29
  @param image The image to scale down
30
30
  @param completionHandler called after successfully scaling down an image
31
31
  @param scalingFactor the scaling factor in range 0.01..1.0. A value of 1.0 won't perform scaling at all
32
- @param compressionQuality the compression quality in range 0.0..1.0 (0.0 for max. compression and 1.0 for lossless compression)
33
- Only applicable for UTTypeJPEG
34
32
  */
35
- - (void)submitImage:(NSData *)image
36
- scalingFactor:(CGFloat)scalingFactor
37
- compressionQuality:(CGFloat)compressionQuality
38
- completionHandler:(void (^)(NSData *))completionHandler;
33
+ - (void)submitImageData:(NSData *)image
34
+ scalingFactor:(CGFloat)scalingFactor
35
+ completionHandler:(void (^)(NSData *))completionHandler;
39
36
 
40
37
  /**
41
38
  Scales and crops the source image
42
39
 
43
40
  @param image The source image data
44
41
  @param uti Either UTTypePNG or UTTypeJPEG
45
- @param rect The cropping rectange for the screenshot. The value is expected to be non-scaled one
46
- since it happens after scaling/orientation change.
47
- CGRectNull could be used to take a screenshot of the full screen.
48
42
  @param scalingFactor Scaling factor in range 0.01..1.0. A value of 1.0 won't perform scaling at all
49
43
  @param compressionQuality the compression quality in range 0.0..1.0 (0.0 for max. compression and 1.0 for lossless compression).
50
44
  Only works if UTI is set to kUTTypeJPEG
51
45
  @param error The actual error instance if the returned result is nil
52
46
  @returns Processed image data compressed according to the given UTI or nil in case of a failure
53
47
  */
54
- - (nullable NSData *)scaledImageWithImage:(NSData *)image
55
- uti:(UTType *)uti
56
- rect:(CGRect)rect
57
- scalingFactor:(CGFloat)scalingFactor
58
- compressionQuality:(CGFloat)compressionQuality
59
- error:(NSError **)error;
48
+ - (nullable NSData *)scaledImageWithData:(NSData *)image
49
+ uti:(UTType *)uti
50
+ scalingFactor:(CGFloat)scalingFactor
51
+ compressionQuality:(CGFloat)compressionQuality
52
+ error:(NSError **)error;
60
53
 
61
54
  @end
62
55
 
@@ -0,0 +1,171 @@
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
+
10
+ #import "FBImageProcessor.h"
11
+
12
+ #import <ImageIO/ImageIO.h>
13
+ #import <UIKit/UIKit.h>
14
+ @import UniformTypeIdentifiers;
15
+
16
+ #import "FBConfiguration.h"
17
+ #import "FBErrorBuilder.h"
18
+ #import "FBImageUtils.h"
19
+ #import "FBLogger.h"
20
+
21
+ const CGFloat FBMinScalingFactor = 0.01f;
22
+ const CGFloat FBMaxScalingFactor = 1.0f;
23
+ const CGFloat FBMinCompressionQuality = 0.0f;
24
+ const CGFloat FBMaxCompressionQuality = 1.0f;
25
+
26
+ @interface FBImageProcessor ()
27
+
28
+ @property (nonatomic) NSData *nextImage;
29
+ @property (nonatomic, readonly) NSLock *nextImageLock;
30
+ @property (nonatomic, readonly) dispatch_queue_t scalingQueue;
31
+
32
+ @end
33
+
34
+ @implementation FBImageProcessor
35
+
36
+ - (id)init
37
+ {
38
+ self = [super init];
39
+ if (self) {
40
+ _nextImageLock = [[NSLock alloc] init];
41
+ _scalingQueue = dispatch_queue_create("image.scaling.queue", NULL);
42
+ }
43
+ return self;
44
+ }
45
+
46
+ - (void)submitImageData:(NSData *)image
47
+ scalingFactor:(CGFloat)scalingFactor
48
+ completionHandler:(void (^)(NSData *))completionHandler
49
+ {
50
+ [self.nextImageLock lock];
51
+ if (self.nextImage != nil) {
52
+ [FBLogger verboseLog:@"Discarding screenshot"];
53
+ }
54
+ self.nextImage = image;
55
+ [self.nextImageLock unlock];
56
+
57
+ #pragma clang diagnostic push
58
+ #pragma clang diagnostic ignored "-Wcompletion-handler"
59
+ dispatch_async(self.scalingQueue, ^{
60
+ [self.nextImageLock lock];
61
+ NSData *nextImageData = self.nextImage;
62
+ self.nextImage = nil;
63
+ [self.nextImageLock unlock];
64
+ if (nextImageData == nil) {
65
+ return;
66
+ }
67
+
68
+ // We do not want this value to be too high because then we get images larger in size than original ones
69
+ // Although, we also don't want to lose too much of the quality on recompression
70
+ CGFloat recompressionQuality = MAX(0.9,
71
+ MIN(FBMaxCompressionQuality, FBConfiguration.mjpegServerScreenshotQuality / 100.0));
72
+ NSData *thumbnailData = [self.class fixedImageDataWithImageData:nextImageData
73
+ scalingFactor:scalingFactor
74
+ uti:UTTypeJPEG
75
+ compressionQuality:recompressionQuality
76
+ // iOS always returns screnshots in portrait orientation, but puts the real value into the metadata
77
+ // Use it with care. See https://github.com/appium/WebDriverAgent/pull/812
78
+ fixOrientation:FBConfiguration.mjpegShouldFixOrientation
79
+ desiredOrientation:nil];
80
+ completionHandler(thumbnailData ?: nextImageData);
81
+ });
82
+ #pragma clang diagnostic pop
83
+ }
84
+
85
+ + (nullable NSData *)fixedImageDataWithImageData:(NSData *)imageData
86
+ scalingFactor:(CGFloat)scalingFactor
87
+ uti:(UTType *)uti
88
+ compressionQuality:(CGFloat)compressionQuality
89
+ fixOrientation:(BOOL)fixOrientation
90
+ desiredOrientation:(nullable NSNumber *)orientation
91
+ {
92
+ scalingFactor = MAX(FBMinScalingFactor, MIN(FBMaxScalingFactor, scalingFactor));
93
+ BOOL usesScaling = scalingFactor > 0.0 && scalingFactor < FBMaxScalingFactor;
94
+ @autoreleasepool {
95
+ if (!usesScaling && !fixOrientation) {
96
+ return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
97
+ }
98
+
99
+ UIImage *image = [UIImage imageWithData:imageData];
100
+ if (nil == image
101
+ || ((image.imageOrientation == UIImageOrientationUp || !fixOrientation) && !usesScaling)) {
102
+ return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
103
+ }
104
+
105
+ CGSize scaledSize = CGSizeMake(image.size.width * scalingFactor, image.size.height * scalingFactor);
106
+ if (!fixOrientation && usesScaling) {
107
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
108
+ __block UIImage *result = nil;
109
+ [image prepareThumbnailOfSize:scaledSize
110
+ completionHandler:^(UIImage * _Nullable thumbnail) {
111
+ result = thumbnail;
112
+ dispatch_semaphore_signal(semaphore);
113
+ }];
114
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
115
+ if (nil == result) {
116
+ return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
117
+ }
118
+ return [uti conformsToType:UTTypePNG]
119
+ ? UIImagePNGRepresentation(result)
120
+ : UIImageJPEGRepresentation(result, compressionQuality);
121
+ }
122
+
123
+ UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init];
124
+ format.scale = scalingFactor;
125
+ UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:scaledSize
126
+ format:format];
127
+ UIImageOrientation desiredOrientation = orientation == nil
128
+ ? image.imageOrientation
129
+ : (UIImageOrientation)orientation.integerValue;
130
+ UIImage *uiImage = [UIImage imageWithCGImage:(CGImageRef)image.CGImage
131
+ scale:image.scale
132
+ orientation:desiredOrientation];
133
+ return [uti conformsToType:UTTypePNG]
134
+ ? [renderer PNGDataWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
135
+ [uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
136
+ }]
137
+ : [renderer JPEGDataWithCompressionQuality:compressionQuality
138
+ actions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
139
+ [uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
140
+ }];
141
+ }
142
+ }
143
+
144
+ - (nullable NSData *)scaledImageWithData:(NSData *)imageData
145
+ uti:(UTType *)uti
146
+ scalingFactor:(CGFloat)scalingFactor
147
+ compressionQuality:(CGFloat)compressionQuality
148
+ error:(NSError **)error
149
+ {
150
+ NSNumber *orientation = nil;
151
+ #if !TARGET_OS_TV
152
+ if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortrait) {
153
+ orientation = @(UIImageOrientationUp);
154
+ } else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortraitUpsideDown) {
155
+ orientation = @(UIImageOrientationDown);
156
+ } else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeLeft) {
157
+ orientation = @(UIImageOrientationRight);
158
+ } else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeRight) {
159
+ orientation = @(UIImageOrientationLeft);
160
+ }
161
+ #endif
162
+ NSData *resultData = [self.class fixedImageDataWithImageData:imageData
163
+ scalingFactor:scalingFactor
164
+ uti:uti
165
+ compressionQuality:compressionQuality
166
+ fixOrientation:YES
167
+ desiredOrientation:orientation];
168
+ return resultData ?: imageData;
169
+ }
170
+
171
+ @end
@@ -17,4 +17,10 @@ BOOL FBIsPngImage(NSData *imageData);
17
17
  /*! Converts the given image data to a PNG representation if necessary */
18
18
  NSData *_Nullable FBToPngData(NSData *imageData);
19
19
 
20
+ /*! Returns YES if the data contains a JPG image */
21
+ BOOL FBIsJpegImage(NSData *imageData);
22
+
23
+ /*! Converts the given image data to a JPG representation if necessary */
24
+ NSData *_Nullable FBToJpegData(NSData *imageData, CGFloat compressionQuality);
25
+
20
26
  NS_ASSUME_NONNULL_END
@@ -12,8 +12,11 @@
12
12
  #import "FBMacros.h"
13
13
  #import "FBConfiguration.h"
14
14
 
15
+ // https://en.wikipedia.org/wiki/List_of_file_signatures
15
16
  static uint8_t PNG_MAGIC[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
16
17
  static const NSUInteger PNG_MAGIC_LEN = 8;
18
+ static uint8_t JPG_MAGIC[] = { 0xff, 0xd8, 0xff };
19
+ static const NSUInteger JPG_MAGIC_LEN = 3;
17
20
 
18
21
  BOOL FBIsPngImage(NSData *imageData)
19
22
  {
@@ -45,3 +48,34 @@ NSData *FBToPngData(NSData *imageData) {
45
48
  UIImage *image = [UIImage imageWithData:imageData];
46
49
  return nil == image ? nil : (NSData *)UIImagePNGRepresentation(image);
47
50
  }
51
+
52
+ BOOL FBIsJpegImage(NSData *imageData)
53
+ {
54
+ if (nil == imageData || [imageData length] < JPG_MAGIC_LEN) {
55
+ return NO;
56
+ }
57
+
58
+ static NSData* jpgMagicStartData = nil;
59
+ static dispatch_once_t onceJpgToken;
60
+ dispatch_once(&onceJpgToken, ^{
61
+ jpgMagicStartData = [NSData dataWithBytesNoCopy:(void*)JPG_MAGIC length:JPG_MAGIC_LEN freeWhenDone:NO];
62
+ });
63
+
64
+ #pragma clang diagnostic push
65
+ #pragma clang diagnostic ignored "-Wassign-enum"
66
+ NSRange range = [imageData rangeOfData:jpgMagicStartData options:kNilOptions range:NSMakeRange(0, JPG_MAGIC_LEN)];
67
+ #pragma clang diagnostic pop
68
+ return range.location != NSNotFound;
69
+ }
70
+
71
+ NSData *FBToJpegData(NSData *imageData, CGFloat compressionQuality) {
72
+ if (nil == imageData || [imageData length] < JPG_MAGIC_LEN) {
73
+ return nil;
74
+ }
75
+ if (FBIsJpegImage(imageData)) {
76
+ return imageData;
77
+ }
78
+
79
+ UIImage *image = [UIImage imageWithData:imageData];
80
+ return nil == image ? nil : (NSData *)UIImageJPEGRepresentation(image, compressionQuality);
81
+ }
@@ -17,7 +17,8 @@
17
17
  #import "FBConfiguration.h"
18
18
  #import "FBLogger.h"
19
19
  #import "FBScreenshot.h"
20
- #import "FBImageIOScaler.h"
20
+ #import "FBImageProcessor.h"
21
+ #import "FBImageUtils.h"
21
22
  #import "XCUIScreen.h"
22
23
 
23
24
  static const NSUInteger MAX_FPS = 60;
@@ -31,7 +32,7 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
31
32
 
32
33
  @property (nonatomic, readonly) dispatch_queue_t backgroundQueue;
33
34
  @property (nonatomic, readonly) NSMutableArray<GCDAsyncSocket *> *listeningClients;
34
- @property (nonatomic, readonly) FBImageIOScaler *imageScaler;
35
+ @property (nonatomic, readonly) FBImageProcessor *imageProcessor;
35
36
  @property (nonatomic, readonly) long long mainScreenID;
36
37
 
37
38
  @end
@@ -48,7 +49,7 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
48
49
  dispatch_async(_backgroundQueue, ^{
49
50
  [self streamScreenshot];
50
51
  });
51
- _imageScaler = [[FBImageIOScaler alloc] init];
52
+ _imageProcessor = [[FBImageProcessor alloc] init];
52
53
  _mainScreenID = [XCUIScreen.mainScreen displayID];
53
54
  }
54
55
  return self;
@@ -82,15 +83,11 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
82
83
  }
83
84
  }
84
85
 
85
- CGFloat scalingFactor = [FBConfiguration mjpegScalingFactor] / 100.0f;
86
- BOOL usesScaling = fabs(FBMaxScalingFactor - scalingFactor) > DBL_EPSILON;
87
- CGFloat compressionQuality = FBConfiguration.mjpegServerScreenshotQuality / 100.0f;
88
- // If scaling is applied we perform another JPEG compression after scaling
89
- // To get the desired compressionQuality we need to do a lossless compression here
90
- CGFloat screenshotCompressionQuality = usesScaling ? FBMaxCompressionQuality : compressionQuality;
91
86
  NSError *error;
87
+ CGFloat compressionQuality = MAX(FBMinCompressionQuality,
88
+ MIN(FBMaxCompressionQuality, FBConfiguration.mjpegServerScreenshotQuality / 100.0));
92
89
  NSData *screenshotData = [FBScreenshot takeInOriginalResolutionWithScreenID:self.mainScreenID
93
- compressionQuality:screenshotCompressionQuality
90
+ compressionQuality:compressionQuality
94
91
  uti:UTTypeJPEG
95
92
  timeout:FRAME_TIMEOUT
96
93
  error:&error];
@@ -100,22 +97,18 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
100
97
  return;
101
98
  }
102
99
 
103
- if (usesScaling) {
104
- [self.imageScaler submitImage:screenshotData
105
- scalingFactor:scalingFactor
106
- compressionQuality:compressionQuality
107
- completionHandler:^(NSData * _Nonnull scaled) {
108
- [self sendScreenshot:scaled];
109
- }];
110
- } else {
111
- [self sendScreenshot:screenshotData];
112
- }
100
+ CGFloat scalingFactor = FBConfiguration.mjpegScalingFactor / 100.0;
101
+ [self.imageProcessor submitImageData:screenshotData
102
+ scalingFactor:scalingFactor
103
+ completionHandler:^(NSData * _Nonnull scaled) {
104
+ [self sendScreenshot:scaled];
105
+ }];
113
106
 
114
107
  [self scheduleNextScreenshotWithInterval:timerInterval timeStarted:timeStarted];
115
108
  }
116
109
 
117
110
  - (void)sendScreenshot:(NSData *)screenshotData {
118
- NSString *chunkHeader = [NSString stringWithFormat:@"--BoundaryString\r\nContent-type: image/jpg\r\nContent-Length: %@\r\n\r\n", @(screenshotData.length)];
111
+ NSString *chunkHeader = [NSString stringWithFormat:@"--BoundaryString\r\nContent-type: image/jpeg\r\nContent-Length: %@\r\n\r\n", @(screenshotData.length)];
119
112
  NSMutableData *chunk = [[chunkHeader dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
120
113
  [chunk appendData:screenshotData];
121
114
  [chunk appendData:(id)[@"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
@@ -24,19 +24,6 @@ NS_ASSUME_NONNULL_BEGIN
24
24
  + (nullable NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
25
25
  error:(NSError **)error;
26
26
 
27
- /**
28
- Retrieves non-scaled screenshot of the particular screen rectangle
29
-
30
- @param quality The number in range 0-3, where 0 is PNG (lossless), 3 is HEIC (lossless), 1- low quality JPEG and 2 - high quality JPEG
31
- @param rect The bounding rectange for the screenshot. The value is expected be non-scaled one.
32
- CGRectNull could be used to take a screenshot of the full screen.
33
- @param error If there is an error, upon return contains an NSError object that describes the problem.
34
- @return Device screenshot as PNG-encoded data or nil in case of failure
35
- */
36
- + (nullable NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
37
- rect:(CGRect)rect
38
- error:(NSError **)error;
39
-
40
27
  /**
41
28
  Retrieves non-scaled screenshot of the whole screen
42
29
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  #import "FBConfiguration.h"
15
15
  #import "FBErrorBuilder.h"
16
- #import "FBImageIOScaler.h"
16
+ #import "FBImageProcessor.h"
17
17
  #import "FBLogger.h"
18
18
  #import "FBMacros.h"
19
19
  #import "FBXCodeCompatibility.h"
@@ -59,30 +59,19 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
59
59
  }
60
60
 
61
61
  + (NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
62
- rect:(CGRect)rect
63
62
  error:(NSError **)error
64
63
  {
65
64
  XCUIScreen *mainScreen = XCUIScreen.mainScreen;
66
65
  return [self.class takeWithScreenID:mainScreen.displayID
67
66
  scale:SCREENSHOT_SCALE
68
67
  compressionQuality:[self.class compressionQualityWithQuality:quality]
69
- rect:rect
70
68
  sourceUTI:[self.class imageUtiWithQuality:quality]
71
69
  error:error];
72
70
  }
73
71
 
74
- + (NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
75
- error:(NSError **)error
76
- {
77
- return [self.class takeInOriginalResolutionWithQuality:quality
78
- rect:CGRectNull
79
- error:error];
80
- }
81
-
82
72
  + (NSData *)takeWithScreenID:(long long)screenID
83
73
  scale:(CGFloat)scale
84
74
  compressionQuality:(CGFloat)compressionQuality
85
- rect:(CGRect)rect
86
75
  sourceUTI:(UTType *)uti
87
76
  error:(NSError **)error
88
77
  {
@@ -94,9 +83,8 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
94
83
  if (nil == screenshotData) {
95
84
  return nil;
96
85
  }
97
- return [[[FBImageIOScaler alloc] init] scaledImageWithImage:screenshotData
86
+ return [[[FBImageProcessor alloc] init] scaledImageWithData:screenshotData
98
87
  uti:UTTypePNG
99
- rect:rect
100
88
  scalingFactor:1.0 / scale
101
89
  compressionQuality:FBMaxCompressionQuality
102
90
  error:error];
@@ -17,6 +17,7 @@ extern NSString* const FB_SETTING_USE_COMPACT_RESPONSES;
17
17
  extern NSString* const FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES;
18
18
  extern NSString* const FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY;
19
19
  extern NSString* const FB_SETTING_MJPEG_SERVER_FRAMERATE;
20
+ extern NSString* const FB_SETTING_MJPEG_FIX_ORIENTATION;
20
21
  extern NSString* const FB_SETTING_MJPEG_SCALING_FACTOR;
21
22
  extern NSString* const FB_SETTING_SCREENSHOT_QUALITY;
22
23
  extern NSString* const FB_SETTING_KEYBOARD_AUTOCORRECTION;
@@ -14,6 +14,7 @@ NSString* const FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES = @"elementResponseAttrib
14
14
  NSString* const FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY = @"mjpegServerScreenshotQuality";
15
15
  NSString* const FB_SETTING_MJPEG_SERVER_FRAMERATE = @"mjpegServerFramerate";
16
16
  NSString* const FB_SETTING_MJPEG_SCALING_FACTOR = @"mjpegScalingFactor";
17
+ NSString* const FB_SETTING_MJPEG_FIX_ORIENTATION = @"mjpegFixOrientation";
17
18
  NSString* const FB_SETTING_SCREENSHOT_QUALITY = @"screenshotQuality";
18
19
  NSString* const FB_SETTING_KEYBOARD_AUTOCORRECTION = @"keyboardAutocorrection";
19
20
  NSString* const FB_SETTING_KEYBOARD_PREDICTION = @"keyboardPrediction";
@@ -9,62 +9,62 @@
9
9
 
10
10
  #import <XCTest/XCTest.h>
11
11
 
12
- #import "FBImageIOScaler.h"
12
+ #import "FBImageProcessor.h"
13
13
  #import "FBIntegrationTestCase.h"
14
14
 
15
15
 
16
- @interface FBImageIOScalerTests : FBIntegrationTestCase
16
+ @interface FBImageProcessorTests : FBIntegrationTestCase
17
17
 
18
18
  @property (nonatomic) NSData *originalImage;
19
19
  @property (nonatomic) CGSize originalSize;
20
20
 
21
21
  @end
22
22
 
23
- @implementation FBImageIOScalerTests
23
+ @implementation FBImageProcessorTests
24
24
 
25
25
  - (void)setUp {
26
26
  XCUIApplication *app = [[XCUIApplication alloc] init];
27
27
  [app launch];
28
28
  XCUIScreenshot *screenshot = app.screenshot;
29
29
  self.originalImage = UIImageJPEGRepresentation(screenshot.image, 1.0);
30
- self.originalSize = [FBImageIOScalerTests scaledSizeFromImage:screenshot.image];
30
+ self.originalSize = [FBImageProcessorTests scaledSizeFromImage:screenshot.image];
31
31
  }
32
32
 
33
33
  - (void)testScaling {
34
34
  CGFloat halfScale = 0.5;
35
- CGSize expectedHalfScaleSize = [FBImageIOScalerTests sizeFromSize:self.originalSize scalingFactor:0.5];
35
+ CGSize expectedHalfScaleSize = [FBImageProcessorTests sizeFromSize:self.originalSize scalingFactor:0.5];
36
36
  [self scaleImageWithFactor:halfScale
37
37
  expectedSize:expectedHalfScaleSize];
38
38
 
39
- // 1 is the smalles scaling factor we accept
39
+ // 0 is the smalles scaling factor we accept
40
40
  CGFloat minScale = 0.0;
41
- CGSize expectedMinScaleSize = [FBImageIOScalerTests sizeFromSize:self.originalSize scalingFactor:0.01];
41
+ CGSize expectedMinScaleSize = [FBImageProcessorTests sizeFromSize:self.originalSize scalingFactor:0.01];
42
42
  [self scaleImageWithFactor:minScale
43
43
  expectedSize:expectedMinScaleSize];
44
44
 
45
45
  // For scaling factors above 100 we don't perform any scaling and just return the unmodified image
46
- CGFloat unscaled = 2.0;
47
- [self scaleImageWithFactor:unscaled
46
+ [self scaleImageWithFactor:1.0
47
+ expectedSize:self.originalSize];
48
+ [self scaleImageWithFactor:2.0
48
49
  expectedSize:self.originalSize];
49
50
  }
50
51
 
51
52
  - (void)scaleImageWithFactor:(CGFloat)scalingFactor expectedSize:(CGSize)excpectedSize {
52
- FBImageIOScaler *scaler = [[FBImageIOScaler alloc] init];
53
+ FBImageProcessor *scaler = [[FBImageProcessor alloc] init];
53
54
 
54
55
  id expScaled = [self expectationWithDescription:@"Receive scaled image"];
55
56
 
56
- [scaler submitImage:self.originalImage
57
- scalingFactor:scalingFactor
58
- compressionQuality:1.0
59
- completionHandler:^(NSData *scaled) {
60
- UIImage *scaledImage = [UIImage imageWithData:scaled];
61
- CGSize scaledSize = [FBImageIOScalerTests scaledSizeFromImage:scaledImage];
62
-
63
- XCTAssertEqualWithAccuracy(scaledSize.width, excpectedSize.width, 1.0);
64
- XCTAssertEqualWithAccuracy(scaledSize.height, excpectedSize.height, 1.0);
65
-
66
- [expScaled fulfill];
67
- }];
57
+ [scaler submitImageData:self.originalImage
58
+ scalingFactor:scalingFactor
59
+ completionHandler:^(NSData *scaled) {
60
+ UIImage *scaledImage = [UIImage imageWithData:scaled];
61
+ CGSize scaledSize = [FBImageProcessorTests scaledSizeFromImage:scaledImage];
62
+
63
+ XCTAssertEqualWithAccuracy(scaledSize.width, excpectedSize.width, 1.0);
64
+ XCTAssertEqualWithAccuracy(scaledSize.height, excpectedSize.height, 1.0);
65
+
66
+ [expScaled fulfill];
67
+ }];
68
68
 
69
69
  [self waitForExpectations:@[expScaled]
70
70
  timeout:0.5];
@@ -78,14 +78,12 @@
78
78
  XCTAssertTrue(screenshot.size.width > screenshot.size.height);
79
79
 
80
80
  XCUIScreen *mainScreen = XCUIScreen.mainScreen;
81
- // TODO: This screenshot rotation was not landscape in an iOS 16 beta simulator.
82
81
  UIImage *screenshotExact = ((XCUIScreenshot *)mainScreen.screenshot).image;
83
- XCTAssertEqualWithAccuracy(screenshotExact.size.height * mainScreen.scale,
84
- screenshot.size.height,
85
- FLT_EPSILON);
86
- XCTAssertEqualWithAccuracy(screenshotExact.size.width * mainScreen.scale,
87
- screenshot.size.width,
88
- FLT_EPSILON);
82
+ CGSize realMainScreenSize = screenshotExact.size.height > screenshot.size.width
83
+ ? CGSizeMake(screenshotExact.size.height * mainScreen.scale, screenshotExact.size.width * mainScreen.scale)
84
+ : CGSizeMake(screenshotExact.size.width * mainScreen.scale, screenshotExact.size.height * mainScreen.scale);
85
+ XCTAssertEqualWithAccuracy(realMainScreenSize.height, screenshot.size.height, FLT_EPSILON);
86
+ XCTAssertEqualWithAccuracy(realMainScreenSize.width, screenshot.size.width, FLT_EPSILON);
89
87
  }
90
88
 
91
89
  - (void)testWifiAddress
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-webdriveragent",
3
- "version": "5.14.0",
3
+ "version": "5.15.2",
4
4
  "description": "Package bundling WebDriverAgent",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -90,7 +90,7 @@
90
90
  "semantic-release": "^22.0.5",
91
91
  "sinon": "^17.0.0",
92
92
  "ts-node": "^10.9.1",
93
- "typescript": "^5.1.6"
93
+ "typescript": "~5.2"
94
94
  },
95
95
  "dependencies": {
96
96
  "@appium/base-driver": "^9.0.0",
@@ -1,195 +0,0 @@
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
-
10
- #import "FBImageIOScaler.h"
11
-
12
- #import <ImageIO/ImageIO.h>
13
- #import <UIKit/UIKit.h>
14
- @import UniformTypeIdentifiers;
15
-
16
- #import "FBConfiguration.h"
17
- #import "FBErrorBuilder.h"
18
- #import "FBLogger.h"
19
-
20
- const CGFloat FBMinScalingFactor = 0.01f;
21
- const CGFloat FBMaxScalingFactor = 1.0f;
22
- const CGFloat FBMinCompressionQuality = 0.0f;
23
- const CGFloat FBMaxCompressionQuality = 1.0f;
24
-
25
- @interface FBImageIOScaler ()
26
-
27
- @property (nonatomic) NSData *nextImage;
28
- @property (nonatomic, readonly) NSLock *nextImageLock;
29
- @property (nonatomic, readonly) dispatch_queue_t scalingQueue;
30
-
31
- @end
32
-
33
- @implementation FBImageIOScaler
34
-
35
- - (id)init
36
- {
37
- self = [super init];
38
- if (self) {
39
- _nextImageLock = [[NSLock alloc] init];
40
- _scalingQueue = dispatch_queue_create("image.scaling.queue", NULL);
41
- }
42
- return self;
43
- }
44
-
45
- - (void)submitImage:(NSData *)image
46
- scalingFactor:(CGFloat)scalingFactor
47
- compressionQuality:(CGFloat)compressionQuality
48
- completionHandler:(void (^)(NSData *))completionHandler
49
- {
50
- [self.nextImageLock lock];
51
- if (self.nextImage != nil) {
52
- [FBLogger verboseLog:@"Discarding screenshot"];
53
- }
54
- scalingFactor = MAX(FBMinScalingFactor, MIN(FBMaxScalingFactor, scalingFactor));
55
- compressionQuality = MAX(FBMinCompressionQuality, MIN(FBMaxCompressionQuality, compressionQuality));
56
- self.nextImage = image;
57
- [self.nextImageLock unlock];
58
-
59
- #pragma clang diagnostic push
60
- #pragma clang diagnostic ignored "-Wcompletion-handler"
61
- dispatch_async(self.scalingQueue, ^{
62
- [self.nextImageLock lock];
63
- NSData *next = self.nextImage;
64
- self.nextImage = nil;
65
- [self.nextImageLock unlock];
66
- if (next == nil) {
67
- return;
68
- }
69
-
70
- NSError *error;
71
- NSData *scaled = [self scaledJpegImageWithImage:next
72
- scalingFactor:scalingFactor
73
- compressionQuality:compressionQuality
74
- error:&error];
75
- if (scaled == nil) {
76
- [FBLogger logFmt:@"%@", error.description];
77
- return;
78
- }
79
- completionHandler(scaled);
80
- });
81
- #pragma clang diagnostic pop
82
- }
83
-
84
- // This method is more optimized for JPEG scaling
85
- // and should be used in `submitImage` API, while the `scaledImageWithImage`
86
- // one is more generic
87
- - (nullable NSData *)scaledJpegImageWithImage:(NSData *)image
88
- scalingFactor:(CGFloat)scalingFactor
89
- compressionQuality:(CGFloat)compressionQuality
90
- error:(NSError **)error
91
- {
92
- CGImageSourceRef imageData = CGImageSourceCreateWithData((CFDataRef)image, nil);
93
- CGSize size = [self.class imageSizeWithImage:imageData];
94
- CGFloat scaledMaxPixelSize = MAX(size.width, size.height) * scalingFactor;
95
- CFDictionaryRef params = (__bridge CFDictionaryRef)@{
96
- (const NSString *)kCGImageSourceCreateThumbnailWithTransform: @(YES),
97
- (const NSString *)kCGImageSourceCreateThumbnailFromImageIfAbsent: @(YES),
98
- (const NSString *)kCGImageSourceThumbnailMaxPixelSize: @(scaledMaxPixelSize)
99
- };
100
- CGImageRef scaled = CGImageSourceCreateThumbnailAtIndex(imageData, 0, params);
101
- CFRelease(imageData);
102
- if (nil == scaled) {
103
- [[[FBErrorBuilder builder]
104
- withDescriptionFormat:@"Failed to scale the image"]
105
- buildError:error];
106
- return nil;
107
- }
108
- NSData *resData = [self jpegDataWithImage:scaled
109
- compressionQuality:compressionQuality];
110
- if (nil == resData) {
111
- [[[FBErrorBuilder builder]
112
- withDescriptionFormat:@"Failed to compress the image to JPEG format"]
113
- buildError:error];
114
- }
115
- CGImageRelease(scaled);
116
- return resData;
117
- }
118
-
119
- - (nullable NSData *)scaledImageWithImage:(NSData *)image
120
- uti:(UTType *)uti
121
- rect:(CGRect)rect
122
- scalingFactor:(CGFloat)scalingFactor
123
- compressionQuality:(CGFloat)compressionQuality
124
- error:(NSError **)error
125
- {
126
- UIImage *uiImage = [UIImage imageWithData:image];
127
- CGSize size = uiImage.size;
128
- CGSize scaledSize = CGSizeMake(size.width * scalingFactor, size.height * scalingFactor);
129
- UIGraphicsBeginImageContext(scaledSize);
130
- UIImageOrientation orientation = uiImage.imageOrientation;
131
- #if !TARGET_OS_TV
132
- if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortrait) {
133
- orientation = UIImageOrientationUp;
134
- } else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortraitUpsideDown) {
135
- orientation = UIImageOrientationDown;
136
- } else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeLeft) {
137
- orientation = UIImageOrientationRight;
138
- } else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeRight) {
139
- orientation = UIImageOrientationLeft;
140
- }
141
- #endif
142
- uiImage = [UIImage imageWithCGImage:(CGImageRef)uiImage.CGImage
143
- scale:uiImage.scale
144
- orientation:orientation];
145
- [uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
146
- UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
147
- UIGraphicsEndImageContext();
148
-
149
- if (!CGRectIsNull(rect)) {
150
- UIGraphicsBeginImageContext(rect.size);
151
- [resultImage drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
152
- resultImage = UIGraphicsGetImageFromCurrentImageContext();
153
- UIGraphicsEndImageContext();
154
- }
155
-
156
- return [uti conformsToType:UTTypePNG]
157
- ? UIImagePNGRepresentation(resultImage)
158
- : UIImageJPEGRepresentation(resultImage, compressionQuality);
159
- }
160
-
161
- - (nullable NSData *)jpegDataWithImage:(CGImageRef)imageRef
162
- compressionQuality:(CGFloat)compressionQuality
163
- {
164
- NSMutableData *newImageData = [NSMutableData data];
165
- CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData(
166
- (__bridge CFMutableDataRef) newImageData,
167
- (__bridge CFStringRef) UTTypeJPEG.identifier,
168
- 1,
169
- NULL);
170
- CFDictionaryRef compressionOptions = (__bridge CFDictionaryRef)@{
171
- (const NSString *)kCGImageDestinationLossyCompressionQuality: @(compressionQuality)
172
- };
173
- CGImageDestinationAddImage(imageDestination, imageRef, compressionOptions);
174
- if(!CGImageDestinationFinalize(imageDestination)) {
175
- [FBLogger log:@"Failed to write the image"];
176
- newImageData = nil;
177
- }
178
- CFRelease(imageDestination);
179
- return newImageData;
180
- }
181
-
182
- + (CGSize)imageSizeWithImage:(CGImageSourceRef)imageSource
183
- {
184
- NSDictionary *options = @{
185
- (const NSString *)kCGImageSourceShouldCache: @(NO)
186
- };
187
- CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (CFDictionaryRef)options);
188
- NSNumber *width = [(__bridge NSDictionary *)properties objectForKey:(const NSString *)kCGImagePropertyPixelWidth];
189
- NSNumber *height = [(__bridge NSDictionary *)properties objectForKey:(const NSString *)kCGImagePropertyPixelHeight];
190
- CGSize size = CGSizeMake([width floatValue], [height floatValue]);
191
- CFRelease(properties);
192
- return size;
193
- }
194
-
195
- @end