blob: 79a272ebe1d0d90a129efad72cb093346da0bf92 [file]
//
// WebDriverResource.m
// iWebDriver
//
// Copyright 2009 Google Inc.
// Copyright 2011 Software Freedom Conservancy.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "WebDriverResource.h"
#import "WebDriverResponse.h"
#import "MainViewController.h"
#import "NSObject+SBJson.h"
#import "errorcodes.h"
@implementation WebDriverResource
@synthesize session = session_,
allowOptionalArguments = allowOptionalArguments_;
- (id)initWithTarget:(id)target
actions:(NSDictionary *)actionTable
{
if (![super init])
return nil;
target_ = [target retain];
methodActions_ = [actionTable retain];
allowOptionalArguments_ = NO;
return self;
}
- (id)initWithTarget:(id)target
GETAction:(SEL)getAction
POSTAction:(SEL)postAction
PUTAction:(SEL)putAction
DELETEAction:(SEL)deleteAction
{
NSMutableDictionary *actions = [NSMutableDictionary dictionary];
if (getAction != NULL)
[actions setValue:[NSValue valueWithPointer:getAction] forKey:@"GET"];
if (postAction != NULL)
[actions setValue:[NSValue valueWithPointer:postAction] forKey:@"POST"];
if (putAction != NULL)
[actions setValue:[NSValue valueWithPointer:putAction] forKey:@"PUT"];
if (deleteAction != NULL)
[actions setValue:[NSValue valueWithPointer:deleteAction] forKey:@"DELETE"];
return [self initWithTarget:target actions:actions];
}
+ (WebDriverResource *)resourceWithTarget:(id)target
GETAction:(SEL)getAction
POSTAction:(SEL)postAction
PUTAction:(SEL)putAction
DELETEAction:(SEL)deleteAction {
return [[[self alloc] initWithTarget:target
GETAction:getAction
POSTAction:postAction
PUTAction:putAction
DELETEAction:deleteAction] autorelease];
}
// Helper method for people who don't care about put and delete
+ (WebDriverResource *)resourceWithTarget:(id)target
GETAction:(SEL)getAction
POSTAction:(SEL)postAction {
return [self resourceWithTarget:target
GETAction:getAction
POSTAction:postAction
PUTAction:NULL
DELETEAction:NULL];
}
- (void)dealloc
{
[target_ release];
[methodActions_ release];
[self setSession:nil];
[super dealloc];
}
#pragma mark Creating a response
// Set the response's session
- (void)configureWebDriverResponse:(WebDriverResponse *)response {
[response setSessionId:session_];
}
// Make an dictionary of objects containing the method arguments. Return nil on
// error.
- (NSDictionary *)getArgumentDictionaryFromData:(NSData *)data {
id requestData = nil;
if ([data length] > 0) {
NSString *dataString =
[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
requestData = [dataString JSONValue];
[dataString release];
}
// The request data should contain an array of arguments.
if (requestData != nil
&& ![requestData isKindOfClass:[NSDictionary class]]) {
NSLog(@"Invalid argument list - Expecting a dictionary but given %@",
requestData);
return nil;
}
return (NSDictionary *)requestData;
}
// Create a WebDriver response from a given selector.
- (WebDriverResponse *)createResponseFromSelector:(SEL)selector
signature:(NSMethodSignature *)method
arguments:(NSDictionary *)arguments {
WebDriverResponse *response;
id result;
@try {
if (arguments != nil) {
// The first two arguments in the method are the target and selector.
// NSInvocation will fill them in for us. The 3rd argument should be
// a dictionary of additional command parameters, initialized from the
// request JSON data.
if ([method numberOfArguments] > 2) {
result = objc_msgSend(target_, selector, arguments);
}
} else {
result = objc_msgSend(target_, selector);
}
if ([method methodReturnLength] == 0) {
response = [WebDriverResponse responseWithValue:nil];
} else {
response = [WebDriverResponse responseWithValue:result];
}
}
@catch (NSException * e) {
NSLog(@"Method invocation error: %@", e);
response = [WebDriverResponse responseWithError:e];
// For easy debugging with Xcode, rethrow the exception here.
}
return response;
}
// Get the HTTP response to this request. This method is part of the
// |HTTPResource| protocol. It is the local entrypoint for creating a response.
- (id<HTTPResponse,NSObject>)httpResponseForQuery:(NSString *)query
method:(NSString *)method
withData:(NSData *)theData {
SEL selector = [[methodActions_ objectForKey:method] pointerValue];
NSMethodSignature *methodSignature = [target_ methodSignatureForSelector:selector];
WebDriverResponse *response = nil;
if (methodSignature == nil) {
// Return a "405 Method Not Allowed" message. We should be setting an
// "Allowed" header whose value is the list of methods supported by this
// resource, but the CocoaHTTPServer framework we're using doesn't seem to
// support it.
// TODO: patch the server for headers
response = [WebDriverResponse responseWithError:
[NSString stringWithFormat:
@"Invalid method for resource: %@ %@", method, query]];
[response setStatus:405];
[self configureWebDriverResponse:response];
return response;
}
NSDictionary *arguments = [self getArgumentDictionaryFromData:theData];
if (arguments != nil) {
[arguments setValue:session_ forKey:@"sessionId"];
}
[[MainViewController sharedInstance]
describeLastAction:NSStringFromSelector(selector)];
// Create response
response = [self createResponseFromSelector:selector
signature:methodSignature
arguments:arguments];
[self configureWebDriverResponse:response];
return response;
}
// This is part of the |HTTPResource| protocol.
- (id<HTTPResource>)elementWithQuery:(NSString *)query {
return self;
}
@end
@implementation HTTPVirtualDirectory (WebDriverResource)
// Helper method to set JSON resources.
- (void)setMyWebDriverHandlerWithGETAction:(SEL)getAction
POSTAction:(SEL)postAction
PUTAction:(SEL)putAction
DELETEAction:(SEL)deleteAction
withName:(NSString *)name {
[self setResource:[WebDriverResource resourceWithTarget:self
GETAction:getAction
POSTAction:postAction
PUTAction:putAction
DELETEAction:deleteAction]
withName:name];
}
// Calls above, but without put and delete.
- (void)setMyWebDriverHandlerWithGETAction:(SEL)getAction
POSTAction:(SEL)postAction
withName:(NSString *)name {
[self setMyWebDriverHandlerWithGETAction:getAction
POSTAction:postAction
PUTAction:NULL
DELETEAction:NULL
withName:name];
}
@end