diff options
Diffstat (limited to 'WebKitTools/DumpRenderTree/mac')
9 files changed, 250 insertions, 35 deletions
diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm index 67e0fa4..4d2da6e 100644 --- a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -62,3 +62,14 @@ void AccessibilityController::setLogScrollingStartEvents(bool) { } +void AccessibilityController::setLogValueChangeEvents(bool) +{ +} + +void AccessibilityController::addNotificationListener(PlatformUIElement, JSObjectRef) +{ +} + +void AccessibilityController::notificationReceived(PlatformUIElement, const std::string&) +{ +} diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm index 948f379..e9361f2 100644 --- a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm @@ -28,6 +28,7 @@ #import "AccessibilityUIElement.h" #import <Foundation/Foundation.h> +#import <JavaScriptCore/JSRetainPtr.h> #import <JavaScriptCore/JSStringRef.h> #import <JavaScriptCore/JSStringRefCF.h> #import <WebKit/WebFrame.h> @@ -52,24 +53,33 @@ #define NSAccessibilityDropEffectsAttribute @"AXDropEffects" #endif -@interface NSObject (WebKitAccessibilityArrayCategory) +typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context); + +@interface NSObject (WebKitAccessibilityAdditions) - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; +- (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context; +- (NSUInteger)accessibilityIndexOfChild:(id)child; @end AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element) : m_element(element) + , m_notificationFunctionCallback(0) { [m_element retain]; } AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other) : m_element(other.m_element) + , m_notificationFunctionCallback(0) { [m_element retain]; } AccessibilityUIElement::~AccessibilityUIElement() { + // Make sure that our notification callback does not stick around. + if (m_notificationFunctionCallback) + [m_element accessibilitySetPostedNotificationCallback:0 withContext:0]; [m_element release]; } @@ -237,6 +247,11 @@ AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y) return AccessibilityUIElement(element); } +unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element) +{ + return [m_element accessibilityIndexOfChild:element->platformUIElement()]; +} + AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index) { Vector<AccessibilityUIElement> children; @@ -337,7 +352,7 @@ JSStringRef AccessibilityUIElement::allAttributes() return [attributes createJSStringRef]; } -JSStringRef AccessibilityUIElement::attributeValue(JSStringRef attribute) +JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute) { id value = [m_element accessibilityAttributeValue:[NSString stringWithJSStringRef:attribute]]; if (![value isKindOfClass:[NSString class]]) @@ -345,6 +360,15 @@ JSStringRef AccessibilityUIElement::attributeValue(JSStringRef attribute) return [value createJSStringRef]; } +bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute) +{ + id value = [m_element accessibilityAttributeValue:[NSString stringWithJSStringRef:attribute]]; + if (![value isKindOfClass:[NSNumber class]]) + return NULL; + + return [value boolValue]; +} + bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) { return [m_element accessibilityIsAttributeSettable:[NSString stringWithJSStringRef:attribute]]; @@ -451,7 +475,7 @@ double AccessibilityUIElement::clickPointY() return static_cast<double>([positionValue pointValue].y); } -double AccessibilityUIElement::intValue() +double AccessibilityUIElement::intValue() const { id value = [m_element accessibilityAttributeValue:NSAccessibilityValueAttribute]; if ([value isKindOfClass:[NSNumber class]]) @@ -529,6 +553,12 @@ bool AccessibilityUIElement::isExpanded() const return false; } +bool AccessibilityUIElement::isChecked() const +{ + // On the Mac, intValue()==1 if a a checkable control is checked. + return intValue() == 1; +} + int AccessibilityUIElement::hierarchicalLevel() const { id value = [m_element accessibilityAttributeValue:NSAccessibilityDisclosureLevelAttribute]; @@ -721,3 +751,87 @@ JSStringRef AccessibilityUIElement::documentURI() { return JSStringCreateWithCharacters(0, 0); } + +JSStringRef AccessibilityUIElement::url() +{ + NSURL *url = [m_element accessibilityAttributeValue:NSAccessibilityURLAttribute]; + return [[url absoluteString] createJSStringRef]; +} + +static void _accessibilityNotificationCallback(id element, NSString* notification, void* context) +{ + if (!context) + return; + + JSObjectRef functionCallback = static_cast<JSObjectRef>(context); + + JSRetainPtr<JSStringRef> jsNotification(Adopt, [notification createJSStringRef]); + JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get()); + JSObjectCallAsFunction([mainFrame globalContext], functionCallback, NULL, 1, &argument, NULL); +} + +bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback) +{ + if (!functionCallback) + return false; + + m_notificationFunctionCallback = functionCallback; + [platformUIElement() accessibilitySetPostedNotificationCallback:_accessibilityNotificationCallback withContext:reinterpret_cast<void*>(m_notificationFunctionCallback)]; + return true; +} + +bool AccessibilityUIElement::isSelectable() const +{ + // FIXME: implement + return false; +} + +bool AccessibilityUIElement::isMultiSelectable() const +{ + // FIXME: implement + return false; +} + +bool AccessibilityUIElement::isVisible() const +{ + // FIXME: implement + return false; +} + +bool AccessibilityUIElement::isOffScreen() const +{ + // FIXME: implement + return false; +} + +bool AccessibilityUIElement::isCollapsed() const +{ + // FIXME: implement + return false; +} + +bool AccessibilityUIElement::hasPopup() const +{ + // FIXME: implement + return false; +} + +void AccessibilityUIElement::takeFocus() +{ + // FIXME: implement +} + +void AccessibilityUIElement::takeSelection() +{ + // FIXME: implement +} + +void AccessibilityUIElement::addSelection() +{ + // FIXME: implement +} + +void AccessibilityUIElement::removeSelection() +{ + // FIXME: implement +} diff --git a/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp b/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp index 85d0613..faef760 100644 --- a/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp +++ b/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2010 Apple Inc. All rights reserved. * (C) 2007 Graham Dennis (graham.dennis@gmail.com) * (C) 2007 Eric Seidel <eric@webkit.org> * @@ -32,6 +32,7 @@ #import "CheckedMalloc.h" #import <malloc/malloc.h> +#import <sys/mman.h> static void* (*savedMalloc)(malloc_zone_t*, size_t); static void* (*savedRealloc)(malloc_zone_t*, void*, size_t); @@ -53,8 +54,19 @@ static void* checkedRealloc(malloc_zone_t* zone, void* ptr, size_t size) void makeLargeMallocFailSilently() { malloc_zone_t* zone = malloc_default_zone(); + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + vm_address_t pageStart = reinterpret_cast<vm_address_t>(zone) & static_cast<vm_size_t>(~(getpagesize() - 1)); + vm_size_t len = reinterpret_cast<vm_address_t>(zone) - pageStart + sizeof(malloc_zone_t); + mprotect(reinterpret_cast<void*>(pageStart), len, PROT_READ | PROT_WRITE); +#endif + savedMalloc = zone->malloc; savedRealloc = zone->realloc; zone->malloc = checkedMalloc; zone->realloc = checkedRealloc; + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + mprotect(reinterpret_cast<void*>(pageStart), len, PROT_READ); +#endif } diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm index 4ffeac3..12e1941 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -80,6 +80,7 @@ #import <objc/objc-runtime.h> #import <wtf/Assertions.h> #import <wtf/RetainPtr.h> +#import <wtf/Threading.h> #import <wtf/OwnPtr.h> using namespace std; @@ -466,6 +467,31 @@ static void setDefaultsToConsistentValuesForTesting() } +static void* runThread(void* arg) +{ + static ThreadIdentifier previousId = 0; + ThreadIdentifier currentId = currentThread(); + // Verify 2 successive threads do not get the same Id. + ASSERT(previousId != currentId); + previousId = currentId; + return 0; +} + +static void testThreadIdentifierMap() +{ + // Imitate 'foreign' threads that are not created by WTF. + pthread_t pthread; + pthread_create(&pthread, 0, &runThread, 0); + pthread_join(pthread, 0); + + pthread_create(&pthread, 0, &runThread, 0); + pthread_join(pthread, 0); + + // Now create another thread using WTF. On OSX, it will have the same pthread handle + // but should get a different ThreadIdentifier. + createThread(runThread, 0, "DumpRenderTree: test"); +} + static void crashHandler(int sig) { char *signalName = strsignal(sig); @@ -614,6 +640,9 @@ void dumpRenderTree(int argc, const char *argv[]) // <rdar://problem/5222911> testStringByEvaluatingJavaScriptFromString(); + // http://webkit.org/b/32689 + testThreadIdentifierMap(); + if (threaded) startJavaScriptThreads(); @@ -1075,15 +1104,20 @@ void dump() } else printf("ERROR: nil result from %s", methodNameStringForFailedTest()); + // Stop the watchdog thread before we leave this test to make sure it doesn't + // fire in between tests causing the next test to fail. + // This is a speculative fix for: https://bugs.webkit.org/show_bug.cgi?id=32339 + invalidateAnyPreviousWaitToDumpWatchdog(); + if (printSeparators) { puts("#EOF"); // terminate the content block fputs("#EOF\n", stderr); } } - + if (dumpPixels && !dumpAsText) dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash()); - + puts("#EOF"); // terminate the (possibly empty) pixels block fflush(stdout); diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h index fe1ac00..5c93ee9 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h @@ -36,6 +36,8 @@ #define BUILDING_ON_TIGER 1 #elif !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 #define BUILDING_ON_LEOPARD 1 +#elif !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define BUILDING_ON_SNOW_LEOPARD 1 #endif @class DumpRenderTreeDraggingInfo; diff --git a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm index 1cba53b..feaeddc 100644 --- a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm +++ b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm @@ -125,11 +125,11 @@ BOOL replayingSavedEvents; || aSelector == @selector(contextClick) || aSelector == @selector(enableDOMUIEventLogging:) || aSelector == @selector(fireKeyboardEventsToElement:) - || aSelector == @selector(keyDown:withModifiers:) + || aSelector == @selector(keyDown:withModifiers:withLocation:) || aSelector == @selector(leapForward:) - || aSelector == @selector(mouseDown:) + || aSelector == @selector(mouseDown:withModifiers:) || aSelector == @selector(mouseMoveToX:Y:) - || aSelector == @selector(mouseUp:) + || aSelector == @selector(mouseUp:withModifiers:) || aSelector == @selector(scheduleAsynchronousClick) || aSelector == @selector(textZoomIn) || aSelector == @selector(textZoomOut) @@ -154,13 +154,13 @@ BOOL replayingSavedEvents; return @"enableDOMUIEventLogging"; if (aSelector == @selector(fireKeyboardEventsToElement:)) return @"fireKeyboardEventsToElement"; - if (aSelector == @selector(keyDown:withModifiers:)) + if (aSelector == @selector(keyDown:withModifiers:withLocation:)) return @"keyDown"; if (aSelector == @selector(leapForward:)) return @"leapForward"; - if (aSelector == @selector(mouseDown:)) + if (aSelector == @selector(mouseDown:withModifiers:)) return @"mouseDown"; - if (aSelector == @selector(mouseUp:)) + if (aSelector == @selector(mouseUp:withModifiers:)) return @"mouseUp"; if (aSelector == @selector(mouseMoveToX:Y:)) return @"mouseMoveTo"; @@ -285,7 +285,26 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti clickCount++; } -- (void)mouseDown:(int)buttonNumber +static int buildModifierFlags(const WebScriptObject* modifiers) +{ + int flags = 0; + if (![modifiers isKindOfClass:[WebScriptObject class]]) + return flags; + for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { + NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i]; + if ([modifierName isEqual:@"ctrlKey"]) + flags |= NSControlKeyMask; + else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"]) + flags |= NSShiftKeyMask; + else if ([modifierName isEqual:@"altKey"]) + flags |= NSAlternateKeyMask; + else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"]) + flags |= NSCommandKeyMask; + } + return flags; +} + +- (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers { [[[mainFrame frameView] documentView] layout]; [self updateClickCountForButton:buttonNumber]; @@ -293,7 +312,7 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown); NSEvent *event = [NSEvent mouseEventWithType:eventType location:lastMousePosition - modifierFlags:0 + modifierFlags:buildModifierFlags(modifiers) timestamp:[self currentEventTime] windowNumber:[[[mainFrame webView] window] windowNumber] context:[NSGraphicsContext currentContext] @@ -309,6 +328,11 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti } } +- (void)mouseDown:(int)buttonNumber +{ + [self mouseDown:buttonNumber withModifiers:nil]; +} + - (void)textZoomIn { [[mainFrame webView] makeTextLarger:self]; @@ -329,13 +353,14 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti [[mainFrame webView] zoomPageOut:self]; } -- (void)mouseUp:(int)buttonNumber +- (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers { if (dragMode && !replayingSavedEvents) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:)]]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]]; [invocation setTarget:self]; - [invocation setSelector:@selector(mouseUp:)]; + [invocation setSelector:@selector(mouseUp:withModifiers:)]; [invocation setArgument:&buttonNumber atIndex:2]; + [invocation setArgument:&modifiers atIndex:3]; [EventSendingController saveEvent:invocation]; [EventSendingController replaySavedEvents]; @@ -347,7 +372,7 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp); NSEvent *event = [NSEvent mouseEventWithType:eventType location:lastMousePosition - modifierFlags:0 + modifierFlags:buildModifierFlags(modifiers) timestamp:[self currentEventTime] windowNumber:[[[mainFrame webView] window] windowNumber] context:[NSGraphicsContext currentContext] @@ -383,6 +408,11 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti } } +- (void)mouseUp:(int)buttonNumber +{ + [self mouseUp:buttonNumber withModifiers:nil]; +} + - (void)mouseMoveToX:(int)x Y:(int)y { if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { @@ -474,7 +504,7 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti savedMouseEvents = nil; } -- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers +- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)keyLocation { NSString *eventCharacter = character; if ([character isEqualToString:@"leftArrow"]) { @@ -524,19 +554,10 @@ static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction acti charactersIgnoringModifiers = [character lowercaseString]; } - if ([modifiers isKindOfClass:[WebScriptObject class]]) { - for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { - NSString *modifier = (NSString *)[modifiers webScriptValueAtIndex:i]; - if ([modifier isEqual:@"ctrlKey"]) - modifierFlags |= NSControlKeyMask; - else if ([modifier isEqual:@"shiftKey"]) - modifierFlags |= NSShiftKeyMask; - else if ([modifier isEqual:@"altKey"]) - modifierFlags |= NSAlternateKeyMask; - else if ([modifier isEqual:@"metaKey"]) - modifierFlags |= NSCommandKeyMask; - } - } + modifierFlags |= buildModifierFlags(modifiers); + + if (keyLocation == DOM_KEY_LOCATION_NUMPAD) + modifierFlags |= NSNumericPadKeyMask; [[[mainFrame frameView] documentView] layout]; diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm index 69fe19f..5f020fa 100644 --- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm @@ -251,6 +251,12 @@ void LayoutTestController::setDatabaseQuota(unsigned long long quota) [origin release]; } +void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool forbidden, JSStringRef scheme) +{ + RetainPtr<CFStringRef> schemeCFString(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, scheme)); + [WebView _setDomainRelaxationForbidden:forbidden forURLScheme:(NSString *)schemeCFString.get()]; +} + void LayoutTestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy) { [WebGeolocationMock setPosition:latitude:longitude:accuracy]; diff --git a/WebKitTools/DumpRenderTree/mac/ObjCController.m b/WebKitTools/DumpRenderTree/mac/ObjCController.m index e0d663e..aa9ee49 100644 --- a/WebKitTools/DumpRenderTree/mac/ObjCController.m +++ b/WebKitTools/DumpRenderTree/mac/ObjCController.m @@ -58,6 +58,7 @@ static void* runJavaScriptThread(void* arg) if (0 || aSelector == @selector(classNameOf:) || aSelector == @selector(objectOfClass:) + || aSelector == @selector(arrayOfString) || aSelector == @selector(identityIsEqual::) || aSelector == @selector(longLongRoundTrip:) || aSelector == @selector(unsignedLongLongRoundTrip:) @@ -77,6 +78,8 @@ static void* runJavaScriptThread(void* arg) return @"className"; if (aSelector == @selector(objectOfClass:)) return @"objectOfClass"; + if (aSelector == @selector(arrayOfString)) + return @"arrayOfString"; if (aSelector == @selector(identityIsEqual::)) return @"identityIsEqual"; if (aSelector == @selector(longLongRoundTrip:)) @@ -122,6 +125,16 @@ static void* runJavaScriptThread(void* arg) return nil; } +- (NSArray *)arrayOfString +{ + NSString *strings[3]; + strings[0] = @"one"; + strings[1] = @"two"; + strings[2] = @"three"; + NSArray *array = [NSArray arrayWithObjects:strings count:3]; + return array; +} + - (BOOL)identityIsEqual:(WebScriptObject *)a :(WebScriptObject *)b { if ([a isKindOfClass:[NSString class]] && [b isKindOfClass:[NSString class]]) @@ -196,10 +209,9 @@ static void* runJavaScriptThread(void* arg) { #if !ASSERT_DISABLED BOOL isWindowObject = [storedWebScriptObject isKindOfClass:[DOMAbstractView class]]; -#endif JSObjectRef jsObject = [storedWebScriptObject JSObject]; ASSERT((jsObject && isWindowObject) || (!jsObject && !isWindowObject)); - +#endif [storedWebScriptObject callWebScriptMethod:@"" withArguments:nil]; [storedWebScriptObject evaluateWebScript:@""]; [storedWebScriptObject setValue:[WebUndefined undefined] forKey:@"key"]; diff --git a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm index 3aaba59..6f82e01 100644 --- a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm +++ b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm @@ -129,6 +129,9 @@ printf("%s\n", [string UTF8String]); } + if (!done && gLayoutTestController->willSendRequestReturnsNull()) + return nil; + if (!done && gLayoutTestController->willSendRequestReturnsNullOnRedirect() && redirectResponse) { printf("Returning null for this redirect\n"); return nil; |