Skip to content
This repository has been archived by the owner on Dec 26, 2019. It is now read-only.

Add Pasteboard support from Appium WebDriverAgent #1064

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions WebDriverAgent.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
71B49EC71ED1A58100D51AD6 /* XCUIElement+FBUID.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B49EC51ED1A58100D51AD6 /* XCUIElement+FBUID.h */; };
71B49EC81ED1A58100D51AD6 /* XCUIElement+FBUID.m in Sources */ = {isa = PBXBuildFile; fileRef = 71B49EC61ED1A58100D51AD6 /* XCUIElement+FBUID.m */; };
71E95ADF1DC101BA002D0364 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7174AF031D9D39AF008C8AD5 /* libxml2.tbd */; };
77D3117D2A08DC33BED2277D /* FBPasteboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 77D3114969541530478E01FD /* FBPasteboard.h */; };
77D3177888989A4B5EDA6C06 /* FBPasteboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D31C2F44395BE1F21099D7 /* FBPasteboard.m */; };
AD35D01A1CF1418E00870A75 /* RoutingHTTPServer.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = AD42DD2B1CF1238500806E5D /* RoutingHTTPServer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
AD35D0641CF1C2C300870A75 /* RoutingHTTPServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD42DD2B1CF1238500806E5D /* RoutingHTTPServer.framework */; };
AD35D06C1CF1C35500870A75 /* WebDriverAgentLib.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = EE158A991CBD452B00A3E3F0 /* WebDriverAgentLib.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -321,8 +323,8 @@
EEE3764A1D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE376481D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m */; };
EEE9B4721CD02B88009D2030 /* FBRunLoopSpinner.h in Headers */ = {isa = PBXBuildFile; fileRef = EEE9B4701CD02B88009D2030 /* FBRunLoopSpinner.h */; settings = {ATTRIBUTES = (Public, ); }; };
EEE9B4731CD02B88009D2030 /* FBRunLoopSpinner.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE9B4711CD02B88009D2030 /* FBRunLoopSpinner.m */; };
EEEA70152110605600C8ADE3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8980D321105B49001789EE /* XCTest.framework */; };
EEEA70152110605600C8ADE2 /* XCTAutomationSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8980D321105B49001789ED /* XCTAutomationSupport.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
EEEA70152110605600C8ADE3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8980D321105B49001789EE /* XCTest.framework */; };
EEEC7C921F21F27A0053426C /* FBPredicate.h in Headers */ = {isa = PBXBuildFile; fileRef = EEEC7C901F21F27A0053426C /* FBPredicate.h */; };
EEEC7C931F21F27A0053426C /* FBPredicate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEEC7C911F21F27A0053426C /* FBPredicate.m */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -447,6 +449,8 @@
71B49EC51ED1A58100D51AD6 /* XCUIElement+FBUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCUIElement+FBUID.h"; sourceTree = "<group>"; };
71B49EC61ED1A58100D51AD6 /* XCUIElement+FBUID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCUIElement+FBUID.m"; sourceTree = "<group>"; };
71E504941DF59BAD0020C32A /* XCUIElementAttributesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XCUIElementAttributesTests.m; sourceTree = "<group>"; };
77D3114969541530478E01FD /* FBPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBPasteboard.h; sourceTree = "<group>"; };
77D31C2F44395BE1F21099D7 /* FBPasteboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBPasteboard.m; sourceTree = "<group>"; };
AD42DD2A1CF121E600806E5D /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
AD42DD2B1CF1238500806E5D /* RoutingHTTPServer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RoutingHTTPServer.framework; path = Carthage/Build/iOS/RoutingHTTPServer.framework; sourceTree = "<group>"; };
AD6C26921CF2379700F8B5FF /* FBAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBAlert.h; path = WebDriverAgentLib/FBAlert.h; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -620,8 +624,8 @@
EE7E271B1D06C69F001BEC7B /* FBXCTestCaseImplementationFailureHoldingProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBXCTestCaseImplementationFailureHoldingProxy.m; sourceTree = "<group>"; };
EE7E27211D06CA91001BEC7B /* libAccessibility.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libAccessibility.tbd; path = usr/lib/libAccessibility.tbd; sourceTree = SDKROOT; };
EE836C021C0F118600D87246 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
EE8980D321105B49001789EE /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
EE8980D321105B49001789ED /* XCTAutomationSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTAutomationSupport.framework; path = Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework; sourceTree = DEVELOPER_DIR; };
EE8980D321105B49001789EE /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
EE8BA9781DCCED9A00A9DEF8 /* FBNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBNavigationController.h; sourceTree = "<group>"; };
EE8BA9791DCCED9A00A9DEF8 /* FBNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBNavigationController.m; sourceTree = "<group>"; };
EE8DDD7820C565FB004D4925 /* XCUIApplicationFBHelpersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XCUIApplicationFBHelpersTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1069,6 +1073,8 @@
EE9AB7991CAEDF0C008C271F /* FBXPathCreator.m */,
EE6B64FB1D0F86EF00E85F5D /* XCTestPrivateSymbols.h */,
EE6B64FC1D0F86EF00E85F5D /* XCTestPrivateSymbols.m */,
77D3114969541530478E01FD /* FBPasteboard.h */,
77D31C2F44395BE1F21099D7 /* FBPasteboard.m */,
);
name = Utilities;
path = WebDriverAgentLib/Utilities;
Expand Down Expand Up @@ -1530,6 +1536,7 @@
EE35AD091E3B77D600A02D78 /* _XCInternalTestRun.h in Headers */,
712A0C871DA3E55D007D02E5 /* FBXPath-Private.h in Headers */,
EE35AD321E3B77D600A02D78 /* XCKeyMappingPath.h in Headers */,
77D3117D2A08DC33BED2277D /* FBPasteboard.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -1845,6 +1852,7 @@
EE35AD7C1E3B80C000A02D78 /* FBXCTestDaemonsProxy.m in Sources */,
EE158AB51CBD456F00A3E3F0 /* XCUIElement+FBTap.m in Sources */,
EE18883B1DA661C400307AA8 /* FBMathUtils.m in Sources */,
77D3177888989A4B5EDA6C06 /* FBPasteboard.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
31 changes: 31 additions & 0 deletions WebDriverAgentLib/Commands/FBCustomCommands.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "FBApplication.h"
#import "FBConfiguration.h"
#import "FBExceptionHandler.h"
#import "FBPasteboard.h"
#import "FBKeyboard.h"
#import "FBResponsePayload.h"
#import "FBRoute.h"
Expand All @@ -40,6 +41,8 @@ + (NSArray *)routes
[[FBRoute POST:@"/wda/keyboard/dismiss"] respondWithTarget:self action:@selector(handleDismissKeyboardCommand:)],
[[FBRoute GET:@"/wda/elementCache/size"] respondWithTarget:self action:@selector(handleGetElementCacheSizeCommand:)],
[[FBRoute POST:@"/wda/elementCache/clear"] respondWithTarget:self action:@selector(handleClearElementCacheCommand:)],
[[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)],
[[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)],
];
}

Expand Down Expand Up @@ -107,4 +110,32 @@ + (NSArray *)routes
[elementCache clear];
return FBResponseWithOK();
}

+ (id<FBResponsePayload>)handleSetPasteboard:(FBRouteRequest *)request
{
NSString *contentType = request.arguments[@"contentType"] ?: @"plaintext";
NSData *content = [[NSData alloc] initWithBase64EncodedString:(NSString *)request.arguments[@"content"]
options:NSDataBase64DecodingIgnoreUnknownCharacters];
if (nil == content) {
return FBResponseWithStatus(FBCommandStatusInvalidArgument, @"Cannot decode the pasteboard content from base64");
}
NSError *error;
if (![FBPasteboard setData:content forType:contentType error:&error]) {
return FBResponseWithError(error);
}
return FBResponseWithOK();
}

+ (id<FBResponsePayload>)handleGetPasteboard:(FBRouteRequest *)request
{
NSString *contentType = request.arguments[@"contentType"] ?: @"plaintext";
NSError *error;
id result = [FBPasteboard dataForType:contentType error:&error];
if (nil == result) {
return FBResponseWithError(error);
}
return FBResponseWithStatus(FBCommandStatusNoError,
[result base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]);
}

@end
39 changes: 39 additions & 0 deletions WebDriverAgentLib/Utilities/FBPasteboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <XCTest/XCTest.h>

NS_ASSUME_NONNULL_BEGIN

@interface FBPasteboard : NSObject

/**
Sets data to the general pasteboard

@param data base64-encoded string containing the data chunk which is going to be written to the pasteboard
@param type one of the possible data types to set: plaintext, url, image
@param error If there is an error, upon return contains an NSError object that describes the problem
@return YES if the operation was successful
*/
+ (BOOL)setData:(NSData *)data forType:(NSString *)type error:(NSError **)error;

/**
Gets the data contained in the general pasteboard

@param type one of the possible data types to get: plaintext, url, image
@param error If there is an error, upon return contains an NSError object that describes the problem
@return NSData object, containing the pasteboard content or an empty string if the pasteboard is empty.
nil is returned if there was an error while getting the data from the pasteboard
*/
+ (nullable NSData *)dataForType:(NSString *)type error:(NSError **)error;

@end

NS_ASSUME_NONNULL_END

83 changes: 83 additions & 0 deletions WebDriverAgentLib/Utilities/FBPasteboard.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "FBPasteboard.h"

#import "FBErrorBuilder.h"

@implementation FBPasteboard

+ (BOOL)setData:(NSData *)data forType:(NSString *)type error:(NSError **)error
{
UIPasteboard *pb = UIPasteboard.generalPasteboard;
if ([type.lowercaseString isEqualToString:@"plaintext"]) {
pb.string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
} else if ([type.lowercaseString isEqualToString:@"image"]) {
UIImage *image = [UIImage imageWithData:data];
if (nil == image) {
NSString *description = @"No image can be parsed from the given pasteboard data";
if (error) {
*error = [[FBErrorBuilder.builder withDescription:description] build];
}
return NO;
}
pb.image = image;
} else if ([type.lowercaseString isEqualToString:@"url"]) {
NSString *urlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:urlString];
if (nil == url) {
NSString *description = @"No URL can be parsed from the given pasteboard data";
if (error) {
*error = [[FBErrorBuilder.builder withDescription:description] build];
}
return NO;
}
pb.URL = url;
} else {
NSString *description = [NSString stringWithFormat:@"Unsupported content type: %@", type];
if (error) {
*error = [[FBErrorBuilder.builder withDescription:description] build];
}
return NO;
}
return YES;
}

+ (NSData *)dataForType:(NSString *)type error:(NSError **)error
{
UIPasteboard *pb = UIPasteboard.generalPasteboard;
if ([type.lowercaseString isEqualToString:@"plaintext"]) {
if (pb.hasStrings) {
return [[pb.strings componentsJoinedByString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding];
}
} else if ([type.lowercaseString isEqualToString:@"image"]) {
if (pb.hasImages) {
return UIImagePNGRepresentation((UIImage *)pb.image);
}
} else if ([type.lowercaseString isEqualToString:@"url"]) {
if (pb.hasURLs) {
NSMutableArray<NSString *> *urls = [NSMutableArray array];
for (NSURL *url in pb.URLs) {
if (nil != url.absoluteString) {
[urls addObject:(id)url.absoluteString];
}
}
return [[urls componentsJoinedByString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding];
}
} else {
NSString *description = [NSString stringWithFormat:@"Unsupported content type: %@", type];
if (error) {
*error = [[FBErrorBuilder.builder withDescription:description] build];
}
return nil;
}
return [@"" dataUsingEncoding:NSUTF8StringEncoding];
}

@end