appium-webdriveragent 16.13.0 → 16.13.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 +12 -0
- package/WebDriverAgentLib/Info.plist +2 -2
- package/WebDriverAgentLib/Routing/FBHTTPServer.m +22 -12
- package/WebDriverAgentLib/Utilities/FBXPath.m +36 -4
- package/WebDriverAgentTests/UnitTests/FBHTTPServerTests.m +27 -0
- package/WebDriverAgentTests/UnitTests/FBXPathTests.m +36 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [16.13.2](https://github.com/appium/WebDriverAgent/compare/v16.13.1...v16.13.2) (2026-09-30)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* process received HTTP bytes before handling EOF ([#1277](https://github.com/appium/WebDriverAgent/issues/1277)) ([203fc4f](https://github.com/appium/WebDriverAgent/commit/203fc4f012ab1d90caf0127fe85f119ad85cb0e8))
|
|
6
|
+
|
|
7
|
+
## [16.13.1](https://github.com/appium/WebDriverAgent/compare/v16.13.0...v16.13.1) (2026-09-30)
|
|
8
|
+
|
|
9
|
+
### Performance Improvements
|
|
10
|
+
|
|
11
|
+
* reuse sibling indices and geometry during XML serialization ([#1283](https://github.com/appium/WebDriverAgent/issues/1283)) ([f352660](https://github.com/appium/WebDriverAgent/commit/f352660deb52728c195a875b4172e38e1a1496b8))
|
|
12
|
+
|
|
1
13
|
## [16.13.0](https://github.com/appium/WebDriverAgent/compare/v16.12.11...v16.13.0) (2026-09-30)
|
|
2
14
|
|
|
3
15
|
### Features
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
<key>CFBundlePackageType</key>
|
|
16
16
|
<string>FMWK</string>
|
|
17
17
|
<key>CFBundleShortVersionString</key>
|
|
18
|
-
<string>16.13.
|
|
18
|
+
<string>16.13.2</string>
|
|
19
19
|
<key>CFBundleSignature</key>
|
|
20
20
|
<string>????</string>
|
|
21
21
|
<key>CFBundleVersion</key>
|
|
22
|
-
<string>16.13.
|
|
22
|
+
<string>16.13.2</string>
|
|
23
23
|
<key>NSPrincipalClass</key>
|
|
24
24
|
<string/>
|
|
25
25
|
</dict>
|
|
@@ -340,18 +340,28 @@ static const int64_t FBStaleConnectionSweepIntervalSec = 10;
|
|
|
340
340
|
|
|
341
341
|
- (void)didClientDisconnect:(nw_connection_t)client
|
|
342
342
|
{
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
343
|
+
// EOF must follow every receive already queued for this client. Otherwise it can
|
|
344
|
+
// remove the buffer before those bytes have even been appended and parsed.
|
|
345
|
+
__weak typeof(self) weakSelf = self;
|
|
346
|
+
dispatch_async(self.bufferProcessingQueue, ^{
|
|
347
|
+
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
348
|
+
if (nil == strongSelf) {
|
|
349
|
+
nw_connection_cancel(client);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
BOOL isAwaitingResponse;
|
|
353
|
+
@synchronized (strongSelf.connectionBuffers) {
|
|
354
|
+
isAwaitingResponse = [strongSelf.connectionsAwaitingResponse containsObject:client];
|
|
355
|
+
[strongSelf.connectionBuffers removeObjectForKey:client];
|
|
356
|
+
[strongSelf.pendingRequestHeaders removeObjectForKey:client];
|
|
357
|
+
[strongSelf.incompleteRequestStarts removeObjectForKey:client];
|
|
358
|
+
}
|
|
359
|
+
// A client that only stopped sending may still read the response to its in-flight request, so
|
|
360
|
+
// that response's send completion closes the connection instead (it finds no buffer left).
|
|
361
|
+
if (!isAwaitingResponse) {
|
|
362
|
+
nw_connection_cancel(client);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
355
365
|
}
|
|
356
366
|
|
|
357
367
|
- (void)client:(nw_connection_t)client didReceiveData:(NSData *)data
|
|
@@ -504,6 +504,18 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
504
504
|
indexPath:(nullable NSString *)indexPath
|
|
505
505
|
includedAttributes:(nullable NSSet<Class> *)includedAttributes
|
|
506
506
|
{
|
|
507
|
+
return [self recordElementAttributes:writer forElement:element indexPath:indexPath
|
|
508
|
+
includedAttributes:includedAttributes siblingIndex:nil];
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
+ (int)recordElementAttributes:(xmlTextWriterPtr)writer
|
|
512
|
+
forElement:(id<FBXCElementSnapshot>)element
|
|
513
|
+
indexPath:(nullable NSString *)indexPath
|
|
514
|
+
includedAttributes:(nullable NSSet<Class> *)includedAttributes
|
|
515
|
+
siblingIndex:(nullable NSNumber *)siblingIndex
|
|
516
|
+
{
|
|
517
|
+
FBXCElementSnapshotWrapper *wrapped = [FBXCElementSnapshotWrapper ensureWrapped:element];
|
|
518
|
+
NSDictionary *rect = nil;
|
|
507
519
|
for (Class attributeCls in FBElementAttribute.supportedAttributes) {
|
|
508
520
|
// include all supported attributes by default unless enumerated explicitly
|
|
509
521
|
if (includedAttributes && ![includedAttributes containsObject:attributeCls]) {
|
|
@@ -519,8 +531,15 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
519
531
|
!FBDoesElementSupportMinMaxValue(element.elementType)) {
|
|
520
532
|
continue;
|
|
521
533
|
}
|
|
522
|
-
int rc
|
|
523
|
-
|
|
534
|
+
int rc;
|
|
535
|
+
if (attributeCls == FBIndexAttribute.class && nil != siblingIndex) {
|
|
536
|
+
rc = [attributeCls recordWithWriter:writer forValue:siblingIndex.stringValue];
|
|
537
|
+
} else if ([attributeCls isSubclassOfClass:FBDimensionAttribute.class]) {
|
|
538
|
+
rect = rect ?: wrapped.wdRect;
|
|
539
|
+
rc = [attributeCls recordWithWriter:writer forValue:[rect[[attributeCls name]] description]];
|
|
540
|
+
} else {
|
|
541
|
+
rc = [attributeCls recordWithWriter:writer forElement:wrapped];
|
|
542
|
+
}
|
|
524
543
|
if (rc < 0) {
|
|
525
544
|
return rc;
|
|
526
545
|
}
|
|
@@ -559,6 +578,17 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
559
578
|
elementStore:(nullable NSMutableDictionary *)elementStore
|
|
560
579
|
includedAttributes:(nullable NSSet<Class> *)includedAttributes
|
|
561
580
|
writer:(xmlTextWriterPtr)writer
|
|
581
|
+
{
|
|
582
|
+
return [self writeXmlWithRootElement:root indexPath:indexPath elementStore:elementStore
|
|
583
|
+
includedAttributes:includedAttributes writer:writer siblingIndex:nil];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
+ (int)writeXmlWithRootElement:(id<FBXCElementSnapshot>)root
|
|
587
|
+
indexPath:(nullable NSString *)indexPath
|
|
588
|
+
elementStore:(nullable NSMutableDictionary *)elementStore
|
|
589
|
+
includedAttributes:(nullable NSSet<Class> *)includedAttributes
|
|
590
|
+
writer:(xmlTextWriterPtr)writer
|
|
591
|
+
siblingIndex:(nullable NSNumber *)siblingIndex
|
|
562
592
|
{
|
|
563
593
|
NSAssert((indexPath == nil && elementStore == nil) || (indexPath != nil && elementStore != nil), @"Either both or none of indexPath and elementStore arguments should be equal to nil", nil);
|
|
564
594
|
|
|
@@ -578,7 +608,8 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
578
608
|
rc = [self recordElementAttributes:writer
|
|
579
609
|
forElement:root
|
|
580
610
|
indexPath:indexPath
|
|
581
|
-
includedAttributes:includedAttributes
|
|
611
|
+
includedAttributes:includedAttributes
|
|
612
|
+
siblingIndex:siblingIndex];
|
|
582
613
|
if (rc < 0) {
|
|
583
614
|
return rc;
|
|
584
615
|
}
|
|
@@ -594,7 +625,8 @@ static NSString *const topNodeIndexPath = @"top";
|
|
|
594
625
|
indexPath:newIndexPath
|
|
595
626
|
elementStore:elementStore
|
|
596
627
|
includedAttributes:includedAttributes
|
|
597
|
-
writer:writer
|
|
628
|
+
writer:writer
|
|
629
|
+
siblingIndex:@(i)];
|
|
598
630
|
if (rc < 0) {
|
|
599
631
|
return rc;
|
|
600
632
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#import <unistd.h>
|
|
15
15
|
|
|
16
16
|
#import "FBHTTPServer.h"
|
|
17
|
+
#import "FBTCPSocket.h"
|
|
17
18
|
|
|
18
19
|
static atomic_int gFramingProbeHits;
|
|
19
20
|
|
|
@@ -242,6 +243,32 @@ static NSString *FBCompleteResponse(NSData *data)
|
|
|
242
243
|
XCTAssertEqual([self serverSocketCountSettlingAt:baseline], baseline);
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
- (void)testEofWaitsForPreviouslyReceivedBytesToBeParsed
|
|
247
|
+
{
|
|
248
|
+
FBHTTPServer *server = [FBHTTPServer new];
|
|
249
|
+
__block NSUInteger calls = 0;
|
|
250
|
+
[server get:@"/probe" withBlock:^(RouteRequest *request, RouteResponse *response) {
|
|
251
|
+
calls++;
|
|
252
|
+
}];
|
|
253
|
+
dispatch_queue_t queue = [server valueForKey:@"bufferProcessingQueue"];
|
|
254
|
+
nw_connection_t client = nw_connection_create(nw_endpoint_create_host("127.0.0.1", "1"),
|
|
255
|
+
nw_parameters_create_secure_tcp(NW_PARAMETERS_DISABLE_PROTOCOL, NW_PARAMETERS_DEFAULT_CONFIGURATION));
|
|
256
|
+
id<FBTCPSocketDelegate> delegate = (id)server;
|
|
257
|
+
[delegate didClientConnect:client];
|
|
258
|
+
dispatch_suspend(queue);
|
|
259
|
+
@try {
|
|
260
|
+
NSData *data = [@"GET /probe HTTP/1.1\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding];
|
|
261
|
+
[delegate client:client didReceiveData:data];
|
|
262
|
+
[delegate didClientDisconnect:client];
|
|
263
|
+
} @finally {
|
|
264
|
+
dispatch_resume(queue);
|
|
265
|
+
}
|
|
266
|
+
dispatch_sync(queue, ^{});
|
|
267
|
+
XCTAssertEqual(calls, 1u);
|
|
268
|
+
[server stop:YES];
|
|
269
|
+
nw_connection_cancel(client);
|
|
270
|
+
}
|
|
271
|
+
|
|
245
272
|
- (void)testHalfClosedClientGetsItsResponseAndIsThenClosed
|
|
246
273
|
{
|
|
247
274
|
// A client may shut down its sending side right after the request and wait for the response.
|
|
@@ -16,11 +16,47 @@
|
|
|
16
16
|
#import "FBXCElementSnapshotWrapper+Helpers.h"
|
|
17
17
|
#import "XCTestPrivateSymbols.h"
|
|
18
18
|
|
|
19
|
+
@interface FBTraversalSnapshot : XCElementSnapshotDouble
|
|
20
|
+
@property (nonatomic, copy) NSArray *childSnapshots;
|
|
21
|
+
@property (nonatomic, weak) FBTraversalSnapshot *parentSnapshot;
|
|
22
|
+
@property (nonatomic) NSUInteger childrenReads;
|
|
23
|
+
@property (nonatomic) NSUInteger frameReads;
|
|
24
|
+
@end
|
|
25
|
+
@implementation FBTraversalSnapshot
|
|
26
|
+
- (NSArray *)children { self.childrenReads++; return self.childSnapshots ?: @[]; }
|
|
27
|
+
- (id)parent { return self.parentSnapshot; }
|
|
28
|
+
- (CGRect)frame { self.frameReads++; return CGRectMake(1, 2, 3, 4); }
|
|
29
|
+
@end
|
|
30
|
+
|
|
19
31
|
@interface FBXPathTests : XCTestCase
|
|
20
32
|
@end
|
|
21
33
|
|
|
22
34
|
@implementation FBXPathTests
|
|
23
35
|
|
|
36
|
+
- (void)testSerializationVisitsSiblingListAndFrameOnce
|
|
37
|
+
{
|
|
38
|
+
FBTraversalSnapshot *root = [FBTraversalSnapshot new];
|
|
39
|
+
NSMutableArray *children = [NSMutableArray array];
|
|
40
|
+
for (NSUInteger index = 0; index < 64; index++) {
|
|
41
|
+
FBTraversalSnapshot *child = [FBTraversalSnapshot new];
|
|
42
|
+
child.parentSnapshot = root;
|
|
43
|
+
[children addObject:child];
|
|
44
|
+
}
|
|
45
|
+
root.childSnapshots = children;
|
|
46
|
+
NSString *query = @"//*[@index >= 0 and @x=1 and @y=2 and @width=3 and @height=4]";
|
|
47
|
+
xmlDocPtr doc = [self documentForSnapshot:root query:query];
|
|
48
|
+
@try {
|
|
49
|
+
XCTAssertEqual(root.childrenReads, 1u);
|
|
50
|
+
XCTAssertEqual(root.frameReads, 1u);
|
|
51
|
+
for (FBTraversalSnapshot *child in children) {
|
|
52
|
+
XCTAssertEqual(child.frameReads, 1u);
|
|
53
|
+
}
|
|
54
|
+
XCTAssertEqualObjects([self xpathStringResultForQuery:@"count(//*[@index=63])" document:doc], @"1");
|
|
55
|
+
} @finally {
|
|
56
|
+
xmlFreeDoc(doc);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
24
60
|
- (NSString *)xmlStringWithElement:(id<FBXCElementSnapshot>)snapshot
|
|
25
61
|
xpathQuery:(nullable NSString *)query
|
|
26
62
|
excludingAttributes:(nullable NSArray<NSString *> *)excludedAttributes
|