diff options
author | Cary Clark <> | 2009-04-14 06:33:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-14 06:33:00 -0700 |
commit | 563af33bc48281d19dce701398dbb88cb54fd7ec (patch) | |
tree | 395b4502f029dea8b25b342d66dc06b5d8f99985 /WebKitTools/DumpRenderTree/mac | |
parent | 5cfedfef172691d0f4bcf2be5ca3cddd8c9a47f4 (diff) | |
download | external_webkit-563af33bc48281d19dce701398dbb88cb54fd7ec.zip external_webkit-563af33bc48281d19dce701398dbb88cb54fd7ec.tar.gz external_webkit-563af33bc48281d19dce701398dbb88cb54fd7ec.tar.bz2 |
AI 146110: add missing files to webkit
brings it in sync with webkit svn cl 42046
Automated import of CL 146110
Diffstat (limited to 'WebKitTools/DumpRenderTree/mac')
47 files changed, 6527 insertions, 0 deletions
diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm new file mode 100644 index 0000000..482c4f3 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" +#import "AccessibilityController.h" + +#import "AccessibilityUIElement.h" +#import <Foundation/Foundation.h> +#import <WebKit/WebFrame.h> +#import <WebKit/WebHTMLView.h> + +AccessibilityController::AccessibilityController() +{ +} + +AccessibilityController::~AccessibilityController() +{ +} + +AccessibilityUIElement AccessibilityController::focusedElement() +{ + // FIXME: we could do some caching here. + id accessibilityObject = [[[mainFrame frameView] documentView] accessibilityFocusedUIElement]; + return AccessibilityUIElement(accessibilityObject); +} + +AccessibilityUIElement AccessibilityController::rootElement() +{ + // FIXME: we could do some caching here. + id accessibilityObject = [[mainFrame frameView] documentView]; + return AccessibilityUIElement(accessibilityObject); +} diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm new file mode 100644 index 0000000..2c4546d --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" +#import "AccessibilityUIElement.h" + +#import <Foundation/Foundation.h> +#import <JavaScriptCore/JSStringRef.h> +#import <JavaScriptCore/JSStringRefCF.h> +#import <WebKit/WebFrame.h> +#import <WebKit/WebHTMLView.h> +#import <WebKit/WebTypesInternal.h> +#import <wtf/RetainPtr.h> +#import <wtf/Vector.h> + +@interface NSObject (WebKitAccessibilityArrayCategory) +- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; +@end + +AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element) + : m_element(element) +{ + [m_element retain]; +} + +AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other) + : m_element(other.m_element) +{ + [m_element retain]; +} + +AccessibilityUIElement::~AccessibilityUIElement() +{ + [m_element release]; +} + +@interface NSString (JSStringRefAdditions) ++ (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef; +- (JSStringRef)createJSStringRef; +@end + +@implementation NSString (JSStringRefAdditions) + ++ (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef +{ + if (!jsStringRef) + return NULL; + + CFStringRef cfString = JSStringCopyCFString(kCFAllocatorDefault, jsStringRef); + return [(NSString *)cfString autorelease]; +} + +- (JSStringRef)createJSStringRef +{ + return JSStringCreateWithCFString((CFStringRef)self); +} + +@end + +static NSString* descriptionOfValue(id valueObject, id focusedAccessibilityObject) +{ + if (!valueObject) + return NULL; + + if ([valueObject isKindOfClass:[NSArray class]]) + return [NSString stringWithFormat:@"<array of size %d>", [(NSArray*)valueObject count]]; + + if ([valueObject isKindOfClass:[NSNumber class]]) + return [(NSNumber*)valueObject stringValue]; + + if ([valueObject isKindOfClass:[NSValue class]]) { + NSString* type = [NSString stringWithCString:[valueObject objCType] encoding:NSASCIIStringEncoding]; + NSValue* value = (NSValue*)valueObject; + if ([type rangeOfString:@"NSRect"].length > 0) + return [NSString stringWithFormat:@"NSRect: %@", NSStringFromRect([value rectValue])]; + if ([type rangeOfString:@"NSPoint"].length > 0) + return [NSString stringWithFormat:@"NSPoint: %@", NSStringFromPoint([value pointValue])]; + if ([type rangeOfString:@"NSSize"].length > 0) + return [NSString stringWithFormat:@"NSSize: %@", NSStringFromSize([value sizeValue])]; + if ([type rangeOfString:@"NSRange"].length > 0) + return [NSString stringWithFormat:@"NSRange: %@", NSStringFromRange([value rangeValue])]; + } + + // Strip absolute URL paths + NSString* description = [valueObject description]; + NSRange range = [description rangeOfString:@"LayoutTests"]; + if (range.length) + return [description substringFromIndex:range.location]; + + // Strip pointer locations + if ([description rangeOfString:@"0x"].length) { + NSString* role = [focusedAccessibilityObject accessibilityAttributeValue:@"AXRole"]; + NSString* title = [focusedAccessibilityObject accessibilityAttributeValue:@"AXTitle"]; + if ([title length]) + return [NSString stringWithFormat:@"<%@: '%@'>", role, title]; + return [NSString stringWithFormat:@"<%@>", role]; + } + + return [valueObject description]; +} + +static NSString* attributesOfElement(id accessibilityObject) +{ + NSArray* supportedAttributes = [accessibilityObject accessibilityAttributeNames]; + + NSMutableString* attributesString = [NSMutableString string]; + for (NSUInteger i = 0; i < [supportedAttributes count]; ++i) { + NSString* attribute = [supportedAttributes objectAtIndex:i]; + + // Right now, position provides useless and screen-specific information, so we do not + // want to include it for the sake of universally passing tests. + if ([attribute isEqualToString:@"AXPosition"]) + continue; + + // accessibilityAttributeValue: can throw an if an attribute is not returned. + // For DumpRenderTree's purpose, we should ignore those exceptions + @try { + id valueObject = [accessibilityObject accessibilityAttributeValue:attribute]; + NSString* value = descriptionOfValue(valueObject, accessibilityObject); + [attributesString appendFormat:@"%@: %@\n", attribute, value]; + } @catch (NSException* e) { } + } + + return attributesString; +} + +static JSStringRef concatenateAttributeAndValue(NSString* attribute, NSString* value) +{ + Vector<UniChar> buffer([attribute length]); + [attribute getCharacters:buffer.data()]; + buffer.append(':'); + buffer.append(' '); + + Vector<UniChar> valueBuffer([value length]); + [value getCharacters:valueBuffer.data()]; + buffer.append(valueBuffer); + + return JSStringCreateWithCharacters(buffer.data(), buffer.size()); +} + +static void convertNSArrayToVector(NSArray* array, Vector<AccessibilityUIElement>& elementVector) +{ + NSUInteger count = [array count]; + for (NSUInteger i = 0; i < count; ++i) + elementVector.append(AccessibilityUIElement([array objectAtIndex:i])); +} + +static JSStringRef descriptionOfElements(Vector<AccessibilityUIElement>& elementVector) +{ + NSMutableString* allElementString = [NSMutableString string]; + size_t size = elementVector.size(); + for (size_t i = 0; i < size; ++i) { + NSString* attributes = attributesOfElement(elementVector[i].platformUIElement()); + [allElementString appendFormat:@"%@\n------------\n", attributes]; + } + + return [allElementString createJSStringRef]; +} + +void AccessibilityUIElement::getLinkedUIElements(Vector<AccessibilityUIElement>& elementVector) +{ + NSArray* linkedElements = [m_element accessibilityAttributeValue:NSAccessibilityLinkedUIElementsAttribute]; + convertNSArrayToVector(linkedElements, elementVector); +} + +void AccessibilityUIElement::getDocumentLinks(Vector<AccessibilityUIElement>& elementVector) +{ + NSArray* linkElements = [m_element accessibilityAttributeValue:@"AXLinkUIElements"]; + convertNSArrayToVector(linkElements, elementVector); +} + +void AccessibilityUIElement::getChildren(Vector<AccessibilityUIElement>& elementVector) +{ + NSArray* children = [m_element accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; + convertNSArrayToVector(children, elementVector); +} + +void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>& elementVector, unsigned location, unsigned length) +{ + NSArray* children = [m_element accessibilityArrayAttributeValues:NSAccessibilityChildrenAttribute index:location maxCount:length]; + convertNSArrayToVector(children, elementVector); +} + +AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index) +{ + Vector<AccessibilityUIElement> children; + getChildrenWithRange(children, index, 1); + + if (children.size() == 1) + return children[0]; + return nil; +} + +AccessibilityUIElement AccessibilityUIElement::titleUIElement() +{ + id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityTitleUIElementAttribute]; + if (accessibilityObject) + return AccessibilityUIElement(accessibilityObject); + + return nil; +} + +AccessibilityUIElement AccessibilityUIElement::parentElement() +{ + id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityParentAttribute]; + if (accessibilityObject) + return AccessibilityUIElement(accessibilityObject); + + return nil; +} + +JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements() +{ + Vector<AccessibilityUIElement> linkedElements; + getLinkedUIElements(linkedElements); + return descriptionOfElements(linkedElements); +} + +JSStringRef AccessibilityUIElement::attributesOfDocumentLinks() +{ + Vector<AccessibilityUIElement> linkElements; + getDocumentLinks(linkElements); + return descriptionOfElements(linkElements); +} + +JSStringRef AccessibilityUIElement::attributesOfChildren() +{ + Vector<AccessibilityUIElement> children; + getChildren(children); + return descriptionOfElements(children); +} + +JSStringRef AccessibilityUIElement::allAttributes() +{ + NSString* attributes = attributesOfElement(m_element); + return [attributes createJSStringRef]; +} + +JSStringRef AccessibilityUIElement::attributeValue(JSStringRef attribute) +{ + id value = [m_element accessibilityAttributeValue:[NSString stringWithJSStringRef:attribute]]; + if (![value isKindOfClass:[NSString class]]) + return NULL; + return [value createJSStringRef]; +} + +bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) +{ + return [m_element accessibilityIsAttributeSettable:[NSString stringWithJSStringRef:attribute]]; +} + +JSStringRef AccessibilityUIElement::parameterizedAttributeNames() +{ + NSArray* supportedParameterizedAttributes = [m_element accessibilityParameterizedAttributeNames]; + + NSMutableString* attributesString = [NSMutableString string]; + for (NSUInteger i = 0; i < [supportedParameterizedAttributes count]; ++i) { + [attributesString appendFormat:@"%@\n", [supportedParameterizedAttributes objectAtIndex:i]]; + } + + return [attributesString createJSStringRef]; +} + +JSStringRef AccessibilityUIElement::role() +{ + NSString* role = descriptionOfValue([m_element accessibilityAttributeValue:@"AXRole"], m_element); + return concatenateAttributeAndValue(@"AXRole", role); +} + +JSStringRef AccessibilityUIElement::title() +{ + NSString* title = descriptionOfValue([m_element accessibilityAttributeValue:@"AXTitle"], m_element); + return concatenateAttributeAndValue(@"AXTitle", title); +} + +JSStringRef AccessibilityUIElement::description() +{ + id description = descriptionOfValue([m_element accessibilityAttributeValue:@"AXDescription"], m_element); + return concatenateAttributeAndValue(@"AXDescription", description); +} + +double AccessibilityUIElement::width() +{ + NSValue* sizeValue = [m_element accessibilityAttributeValue:@"AXSize"]; + return static_cast<double>([sizeValue sizeValue].width); +} + +double AccessibilityUIElement::height() +{ + NSValue* sizeValue = [m_element accessibilityAttributeValue:@"AXSize"]; + return static_cast<double>([sizeValue sizeValue].height); +} + +double AccessibilityUIElement::intValue() +{ + id value = [m_element accessibilityAttributeValue:@"AXValue"]; + if ([value isKindOfClass:[NSNumber class]]) + return [(NSNumber*)value doubleValue]; + return 0.0f; +} + +double AccessibilityUIElement::minValue() +{ + id value = [m_element accessibilityAttributeValue:@"AXMinValue"]; + if ([value isKindOfClass:[NSNumber class]]) + return [(NSNumber*)value doubleValue]; + return 0.0f; +} + +double AccessibilityUIElement::maxValue() +{ + id value = [m_element accessibilityAttributeValue:@"AXMaxValue"]; + if ([value isKindOfClass:[NSNumber class]]) + return [(NSNumber*)value doubleValue]; + return 0.0; +} + +int AccessibilityUIElement::insertionPointLineNumber() +{ + id value = [m_element accessibilityAttributeValue:@"AXInsertionPointLineNumber"]; + if ([value isKindOfClass:[NSNumber class]]) + return [(NSNumber *)value intValue]; + return -1; +} + +bool AccessibilityUIElement::supportsPressAction() +{ + NSArray* actions = [m_element accessibilityActionNames]; + return [actions containsObject:@"AXPress"]; +} + +// parameterized attributes +int AccessibilityUIElement::lineForIndex(int index) +{ + id value = [m_element accessibilityAttributeValue:@"AXLineForIndex" forParameter:[NSNumber numberWithInt:index]]; + if ([value isKindOfClass:[NSNumber class]]) + return [(NSNumber *)value intValue]; + return -1; +} + +JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length) +{ + NSRange range = NSMakeRange(location, length); + id value = [m_element accessibilityAttributeValue:NSAccessibilityBoundsForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]]; + NSRect rect = NSMakeRect(0,0,0,0); + if ([value isKindOfClass:[NSValue class]]) + rect = [value rectValue]; + + // don't return position information because it is platform dependent + NSMutableString* boundsDescription = [NSMutableString stringWithFormat:@"{{%f, %f}, {%f, %f}}",-1.0f,-1.0f,rect.size.width,rect.size.height]; + return [boundsDescription createJSStringRef]; +} + +JSStringRef AccessibilityUIElement::attributesOfColumnHeaders() +{ + // not yet defined in AppKit... odd + NSArray* columnHeadersArray = [m_element accessibilityAttributeValue:@"AXColumnHeaderUIElements"]; + Vector<AccessibilityUIElement> columnHeadersVector; + convertNSArrayToVector(columnHeadersArray, columnHeadersVector); + return descriptionOfElements(columnHeadersVector); +} + +JSStringRef AccessibilityUIElement::attributesOfRowHeaders() +{ + NSArray* rowHeadersArray = [m_element accessibilityAttributeValue:@"AXRowHeaderUIElements"]; + Vector<AccessibilityUIElement> rowHeadersVector; + convertNSArrayToVector(rowHeadersArray, rowHeadersVector); + return descriptionOfElements(rowHeadersVector); +} + +JSStringRef AccessibilityUIElement::attributesOfColumns() +{ + NSArray* columnsArray = [m_element accessibilityAttributeValue:NSAccessibilityColumnsAttribute]; + Vector<AccessibilityUIElement> columnsVector; + convertNSArrayToVector(columnsArray, columnsVector); + return descriptionOfElements(columnsVector); +} + +JSStringRef AccessibilityUIElement::attributesOfRows() +{ + NSArray* rowsArray = [m_element accessibilityAttributeValue:NSAccessibilityRowsAttribute]; + Vector<AccessibilityUIElement> rowsVector; + convertNSArrayToVector(rowsArray, rowsVector); + return descriptionOfElements(rowsVector); +} + +JSStringRef AccessibilityUIElement::attributesOfVisibleCells() +{ + NSArray* cellsArray = [m_element accessibilityAttributeValue:@"AXVisibleCells"]; + Vector<AccessibilityUIElement> cellsVector; + convertNSArrayToVector(cellsArray, cellsVector); + return descriptionOfElements(cellsVector); +} + +JSStringRef AccessibilityUIElement::attributesOfHeader() +{ + id headerObject = [m_element accessibilityAttributeValue:NSAccessibilityHeaderAttribute]; + if (!headerObject) + return [@"" createJSStringRef]; + + Vector<AccessibilityUIElement> headerVector; + headerVector.append(headerObject); + return descriptionOfElements(headerVector); +} + +int AccessibilityUIElement::indexInTable() +{ + NSNumber* indexNumber = [m_element accessibilityAttributeValue:NSAccessibilityIndexAttribute]; + if (!indexNumber) + return -1; + return [indexNumber intValue]; +} + +JSStringRef AccessibilityUIElement::rowIndexRange() +{ + NSValue* indexRange = [m_element accessibilityAttributeValue:@"AXRowIndexRange"]; + NSRange range = indexRange ? [indexRange rangeValue] : NSMakeRange(0,0); + NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length]; + return [rangeDescription createJSStringRef]; +} + +JSStringRef AccessibilityUIElement::columnIndexRange() +{ + NSNumber* indexRange = [m_element accessibilityAttributeValue:@"AXColumnIndexRange"]; + NSRange range = indexRange ? [indexRange rangeValue] : NSMakeRange(0,0); + NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length]; + return [rangeDescription createJSStringRef]; +} + +AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row) +{ + NSArray *colRowArray = [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:col], [NSNumber numberWithUnsignedInt:row], nil]; + return [m_element accessibilityAttributeValue:@"AXCellForColumnAndRow" forParameter:colRowArray]; +} + +JSStringRef AccessibilityUIElement::selectedTextRange() +{ + NSNumber *indexRange = [m_element accessibilityAttributeValue:NSAccessibilitySelectedTextRangeAttribute]; + NSRange range = indexRange ? [indexRange rangeValue] : NSMakeRange(0,0); + NSMutableString *rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length]; + return [rangeDescription createJSStringRef]; +} + +void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length) +{ + NSRange textRange = NSMakeRange(location, length); + NSValue *textRangeValue = [NSValue valueWithRange:textRange]; + [m_element accessibilitySetValue:textRangeValue forAttribute:NSAccessibilitySelectedTextRangeAttribute]; +} diff --git a/WebKitTools/DumpRenderTree/mac/AppleScriptController.h b/WebKitTools/DumpRenderTree/mac/AppleScriptController.h new file mode 100644 index 0000000..c29789c --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/AppleScriptController.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +@class WebView; + +@interface AppleScriptController : NSObject +{ + WebView *webView; +} +- (id)initWithWebView:(WebView *)view; +@end diff --git a/WebKitTools/DumpRenderTree/mac/AppleScriptController.m b/WebKitTools/DumpRenderTree/mac/AppleScriptController.m new file mode 100644 index 0000000..86d2881 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/AppleScriptController.m @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "AppleScriptController.h" + +#import <WebKit/WebView.h> +#import <WebKit/WebViewPrivate.h> // for aeDescByEvaluatingJavaScriptFromString, which is pending API review + +@implementation AppleScriptController + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + if (aSelector == @selector(doJavaScript:)) + return NO; + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(doJavaScript:)) + return @"doJavaScript"; + + return nil; +} + +- (id)initWithWebView:(WebView *)wv +{ + self = [super init]; + webView = wv; + return self; +} + +static id convertAEDescToObject(NSAppleEventDescriptor *aeDesc) +{ + id value = nil; + + DescType descType = [aeDesc descriptorType]; + switch (descType) { + case typeUnicodeText: + value = [NSString stringWithFormat:@"\"%@\"", [aeDesc stringValue]]; + break; + case typeLongDateTime: + if ([[aeDesc data] length] == sizeof(LongDateTime)) { + LongDateTime d; + [[aeDesc data] getBytes:&d]; + value = [NSString stringWithFormat:@"%016llX", (unsigned long long)d]; + } + break; + case typeAEList: + value = [NSMutableString stringWithString:@"("]; + int numItems = [aeDesc numberOfItems]; + for (int i = 0; i < numItems; ++i) { + if (i != 0) + [(NSMutableString*)value appendString:@", "]; + id obj = convertAEDescToObject([aeDesc descriptorAtIndex:(i + 1)]); + [(NSMutableString*)value appendString:[obj description]]; + } + [(NSMutableString*)value appendString:@")"]; + break; + case typeType: { + OSType type = [aeDesc typeCodeValue]; + + char typeStr[5]; + typeStr[0] = type >> 24; + typeStr[1] = type >> 16; + typeStr[2] = type >> 8; + typeStr[3] = type; + typeStr[4] = 0; + + value = [NSString stringWithFormat:@"'%s'", typeStr]; + break; + } + } + + if (!value) + value = [aeDesc stringValue]; + if (!value) + value = [aeDesc data]; + + return value; +} + +- (NSString *)doJavaScript:(NSString *)aString +{ + NSAppleEventDescriptor *aeDesc = [webView aeDescByEvaluatingJavaScriptFromString:aString]; + if (!aeDesc) + return @"(null)"; + + DescType descType = [aeDesc descriptorType]; + char descTypeStr[5]; + descTypeStr[0] = descType >> 24; + descTypeStr[1] = descType >> 16; + descTypeStr[2] = descType >> 8; + descTypeStr[3] = descType; + descTypeStr[4] = 0; + + return [NSString stringWithFormat:@"%@ ('%s')", convertAEDescToObject(aeDesc), descTypeStr]; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp b/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp new file mode 100644 index 0000000..467f1bb --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/CheckedMalloc.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "CheckedMalloc.h" + +#import <malloc/malloc.h> + +static void* (*savedMalloc)(malloc_zone_t*, size_t); +static void* (*savedRealloc)(malloc_zone_t*, void*, size_t); + +static void* checkedMalloc(malloc_zone_t* zone, size_t size) +{ + if (size >= 0x10000000) + return 0; + return savedMalloc(zone, size); +} + +static void* checkedRealloc(malloc_zone_t* zone, void* ptr, size_t size) +{ + if (size >= 0x10000000) + return 0; + return savedRealloc(zone, ptr, size); +} + +void makeLargeMallocFailSilently() +{ + malloc_zone_t* zone = malloc_default_zone(); + savedMalloc = zone->malloc; + savedRealloc = zone->realloc; + zone->malloc = checkedMalloc; + zone->realloc = checkedRealloc; +} diff --git a/WebKitTools/DumpRenderTree/mac/CheckedMalloc.h b/WebKitTools/DumpRenderTree/mac/CheckedMalloc.h new file mode 100644 index 0000000..c03bd20 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/CheckedMalloc.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void makeLargeMallocFailSilently(); diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig new file mode 100644 index 0000000..fce81d3 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig @@ -0,0 +1,17 @@ +HEADER_SEARCH_PATHS = ForwardingHeaders mac/InternalHeaders; +FRAMEWORK_SEARCH_PATHS = $(FRAMEWORK_SEARCH_PATHS_$(MAC_OS_X_VERSION_MAJOR)); +FRAMEWORK_SEARCH_PATHS_ = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks +FRAMEWORK_SEARCH_PATHS_1040 = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks +FRAMEWORK_SEARCH_PATHS_1050 = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks +FRAMEWORK_SEARCH_PATHS_1060 = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks +GCC_PREPROCESSOR_DEFINITIONS = ENABLE_DASHBOARD_SUPPORT; +DEBUG_INFORMATION_FORMAT = dwarf +PREBINDING = NO +GCC_C_LANGUAGE_STANDARD = gnu99 +GCC_PRECOMPILE_PREFIX_HEADER = YES +GCC_TREAT_WARNINGS_AS_ERRORS = YES +GCC_WARN_UNUSED_FUNCTION = YES +GCC_WARN_UNUSED_VARIABLE = YES +GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO +WARNING_CFLAGS = -Wall -W -Wno-unused-parameter +LINKER_DISPLAYS_MANGLED_NAMES = YES; diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig new file mode 100644 index 0000000..eab511f --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig @@ -0,0 +1,15 @@ +#include "Base.xcconfig" + +ARCHS = $(ARCHS_$(MAC_OS_X_VERSION_MAJOR)); +ARCHS_ = $(ARCHS_1040); +ARCHS_1040 = $(NATIVE_ARCH); +ARCHS_1050 = $(NATIVE_ARCH); +ARCHS_1060 = $(ARCHS_STANDARD_32_64_BIT); + +ONLY_ACTIVE_ARCH = YES; + +MACOSX_DEPLOYMENT_TARGET = $(MACOSX_DEPLOYMENT_TARGET_$(MAC_OS_X_VERSION_MAJOR)) +MACOSX_DEPLOYMENT_TARGET_ = 10.4; +MACOSX_DEPLOYMENT_TARGET_1040 = 10.4; +MACOSX_DEPLOYMENT_TARGET_1050 = 10.5; +MACOSX_DEPLOYMENT_TARGET_1060 = 10.6; diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/DumpRenderTree.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/DumpRenderTree.xcconfig new file mode 100644 index 0000000..b977225 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/Configurations/DumpRenderTree.xcconfig @@ -0,0 +1,4 @@ +OTHER_LDFLAGS = -sectcreate __DATA Ahem qt/fonts/AHEM____.TTF -sectcreate __DATA WeightWatcher100 fonts/WebKitWeightWatcher100.ttf -sectcreate __DATA WeightWatcher200 fonts/WebKitWeightWatcher200.ttf -sectcreate __DATA WeightWatcher300 fonts/WebKitWeightWatcher300.ttf -sectcreate __DATA WeightWatcher400 fonts/WebKitWeightWatcher400.ttf -sectcreate __DATA WeightWatcher500 fonts/WebKitWeightWatcher500.ttf -sectcreate __DATA WeightWatcher600 fonts/WebKitWeightWatcher600.ttf -sectcreate __DATA WeightWatcher700 fonts/WebKitWeightWatcher700.ttf -sectcreate __DATA WeightWatcher800 fonts/WebKitWeightWatcher800.ttf -sectcreate __DATA WeightWatcher900 fonts/WebKitWeightWatcher900.ttf +PRODUCT_NAME = DumpRenderTree +GCC_ENABLE_OBJC_EXCEPTIONS = YES +GCC_PREFIX_HEADER = DumpRenderTreePrefix.h diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/ImageDiff.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/ImageDiff.xcconfig new file mode 100644 index 0000000..fcd64c5 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/Configurations/ImageDiff.xcconfig @@ -0,0 +1 @@ +PRODUCT_NAME = ImageDiff diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/TestNetscapePlugIn.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/TestNetscapePlugIn.xcconfig new file mode 100644 index 0000000..ea17629 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/Configurations/TestNetscapePlugIn.xcconfig @@ -0,0 +1,6 @@ +PRODUCT_NAME = TestNetscapePlugIn +WRAPPER_EXTENSION = plugin +INFOPLIST_FILE = TestNetscapePlugIn.subproj/Info.plist +INSTALL_PATH = "$(USER_LIBRARY_DIR)/Plugins" +WARNING_CFLAGS = -Wmost -Wno-four-char-constants -Wno-unknown-pragmas +LIBRARY_STYLE = BUNDLE diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm new file mode 100644 index 0000000..485dece --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" + +#import "AccessibilityController.h" +#import "CheckedMalloc.h" +#import "DumpRenderTreePasteboard.h" +#import "DumpRenderTreeWindow.h" +#import "EditingDelegate.h" +#import "EventSendingController.h" +#import "FrameLoadDelegate.h" +#import "JavaScriptThreading.h" +#import "LayoutTestController.h" +#import "NavigationController.h" +#import "ObjCPlugin.h" +#import "ObjCPluginFunction.h" +#import "PixelDumpSupport.h" +#import "PolicyDelegate.h" +#import "ResourceLoadDelegate.h" +#import "UIDelegate.h" +#import "WorkQueue.h" +#import "WorkQueueItem.h" +#import <Carbon/Carbon.h> +#import <CoreFoundation/CoreFoundation.h> +#import <WebKit/DOMElementPrivate.h> +#import <WebKit/DOMExtensions.h> +#import <WebKit/DOMRange.h> +#import <WebKit/WebBackForwardList.h> +#import <WebKit/WebCache.h> +#import <WebKit/WebCoreStatistics.h> +#import <WebKit/WebDataSourcePrivate.h> +#import <WebKit/WebDatabaseManagerPrivate.h> +#import <WebKit/WebDocumentPrivate.h> +#import <WebKit/WebEditingDelegate.h> +#import <WebKit/WebFrameView.h> +#import <WebKit/WebHTMLRepresentationInternal.h> +#import <WebKit/WebHistory.h> +#import <WebKit/WebHistoryItemPrivate.h> +#import <WebKit/WebInspector.h> +#import <WebKit/WebPluginDatabase.h> +#import <WebKit/WebPreferences.h> +#import <WebKit/WebPreferencesPrivate.h> +#import <WebKit/WebResourceLoadDelegate.h> +#import <WebKit/WebTypesInternal.h> +#import <WebKit/WebViewPrivate.h> +#import <getopt.h> +#import <mach-o/getsect.h> +#import <objc/objc-runtime.h> +#import <wtf/Assertions.h> +#import <wtf/RetainPtr.h> +#import <wtf/OwnPtr.h> + +using namespace std; + +@interface DumpRenderTreeEvent : NSEvent +@end + +static void runTest(const string& testPathOrURL); + +// Deciding when it's OK to dump out the state is a bit tricky. All these must be true: +// - There is no load in progress +// - There is no work queued up (see workQueue var, below) +// - waitToDump==NO. This means either waitUntilDone was never called, or it was called +// and notifyDone was called subsequently. +// Note that the call to notifyDone and the end of the load can happen in either order. + +volatile bool done; + +NavigationController* gNavigationController = 0; +LayoutTestController* gLayoutTestController = 0; + +WebFrame *mainFrame = 0; +// This is the topmost frame that is loading, during a given load, or nil when no load is +// in progress. Usually this is the same as the main frame, but not always. In the case +// where a frameset is loaded, and then new content is loaded into one of the child frames, +// that child frame is the "topmost frame that is loading". +WebFrame *topLoadingFrame = nil; // !nil iff a load is in progress + + +CFMutableSetRef disallowedURLs = 0; +CFRunLoopTimerRef waitToDumpWatchdog = 0; + +// Delegates +static FrameLoadDelegate *frameLoadDelegate; +static UIDelegate *uiDelegate; +static EditingDelegate *editingDelegate; +static ResourceLoadDelegate *resourceLoadDelegate; +PolicyDelegate *policyDelegate; + +static int dumpPixels; +static int threaded; +static int dumpTree = YES; +static int forceComplexText; +static BOOL printSeparators; +static RetainPtr<CFStringRef> persistentUserStyleSheetLocation; + +static WebHistoryItem *prevTestBFItem = nil; // current b/f item at the end of the previous test + +const unsigned maxViewHeight = 600; +const unsigned maxViewWidth = 800; + +#if __OBJC2__ +static void swizzleAllMethods(Class imposter, Class original) +{ + unsigned int imposterMethodCount; + Method* imposterMethods = class_copyMethodList(imposter, &imposterMethodCount); + + unsigned int originalMethodCount; + Method* originalMethods = class_copyMethodList(original, &originalMethodCount); + + for (unsigned int i = 0; i < imposterMethodCount; i++) { + SEL imposterMethodName = method_getName(imposterMethods[i]); + + // Attempt to add the method to the original class. If it fails, the method already exists and we should + // instead exchange the implementations. + if (class_addMethod(original, imposterMethodName, method_getImplementation(imposterMethods[i]), method_getTypeEncoding(imposterMethods[i]))) + continue; + + unsigned int j = 0; + for (; j < originalMethodCount; j++) { + SEL originalMethodName = method_getName(originalMethods[j]); + if (sel_isEqual(imposterMethodName, originalMethodName)) + break; + } + + // If class_addMethod failed above then the method must exist on the original class. + ASSERT(j < originalMethodCount); + method_exchangeImplementations(imposterMethods[i], originalMethods[j]); + } + + free(imposterMethods); + free(originalMethods); +} +#endif + +static void poseAsClass(const char* imposter, const char* original) +{ + Class imposterClass = objc_getClass(imposter); + Class originalClass = objc_getClass(original); + +#if !__OBJC2__ + class_poseAs(imposterClass, originalClass); +#else + + // Swizzle instance methods + swizzleAllMethods(imposterClass, originalClass); + // and then class methods + swizzleAllMethods(object_getClass(imposterClass), object_getClass(originalClass)); +#endif +} + +void setPersistentUserStyleSheetLocation(CFStringRef url) +{ + persistentUserStyleSheetLocation = url; +} + +static bool shouldIgnoreWebCoreNodeLeaks(const string& URLString) +{ + static char* const ignoreSet[] = { + // Keeping this infrastructure around in case we ever need it again. + }; + static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(char*); + + for (int i = 0; i < ignoreSetCount; i++) { + // FIXME: ignore case + string curIgnore(ignoreSet[i]); + // Match at the end of the URLString + if (!URLString.compare(URLString.length() - curIgnore.length(), curIgnore.length(), curIgnore)) + return true; + } + return false; +} + +static void activateFonts() +{ +#if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_TIGER) + static const char* fontSectionNames[] = { + "Ahem", + "WeightWatcher100", + "WeightWatcher200", + "WeightWatcher300", + "WeightWatcher400", + "WeightWatcher500", + "WeightWatcher600", + "WeightWatcher700", + "WeightWatcher800", + "WeightWatcher900", + 0 + }; + + for (unsigned i = 0; fontSectionNames[i]; ++i) { + unsigned long fontDataLength; + char* fontData = getsectdata("__DATA", fontSectionNames[i], &fontDataLength); + if (!fontData) { + fprintf(stderr, "Failed to locate the %s font.\n", fontSectionNames[i]); + exit(1); + } + + ATSFontContainerRef fontContainer; + OSStatus status = ATSFontActivateFromMemory(fontData, fontDataLength, kATSFontContextLocal, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &fontContainer); + + if (status != noErr) { + fprintf(stderr, "Failed to activate the %s font.\n", fontSectionNames[i]); + exit(1); + } + } +#else + + // Work around <rdar://problem/6698023> by activating fonts from disk + // FIXME: This code can be removed once <rdar://problem/6698023> is addressed. + + static const char* fontFileNames[] = { + "AHEM____.TTF", + "WebKitWeightWatcher100.ttf", + "WebKitWeightWatcher200.ttf", + "WebKitWeightWatcher300.ttf", + "WebKitWeightWatcher400.ttf", + "WebKitWeightWatcher500.ttf", + "WebKitWeightWatcher600.ttf", + "WebKitWeightWatcher700.ttf", + "WebKitWeightWatcher800.ttf", + "WebKitWeightWatcher900.ttf", + 0 + }; + + NSMutableArray *fontURLs = [NSMutableArray array]; + NSURL *resourcesDirectory = [NSURL URLWithString:@"DumpRenderTree.resources" relativeToURL:[[NSBundle mainBundle] executableURL]]; + for (unsigned i = 0; fontFileNames[i]; ++i) { + NSURL *fontURL = [resourcesDirectory URLByAppendingPathComponent:[NSString stringWithUTF8String:fontFileNames[i]]]; + [fontURLs addObject:fontURL]; + } + + CFArrayRef errors = 0; + if (!CTFontManagerRegisterFontsForURLs((CFArrayRef)fontURLs, kCTFontManagerScopeProcess, &errors)) { + NSLog(@"Failed to activate fonts: %@", errors); + CFRelease(errors); + exit(1); + } +#endif +} + +WebView *createWebViewAndOffscreenWindow() +{ + NSRect rect = NSMakeRect(0, 0, maxViewWidth, maxViewHeight); + WebView *webView = [[WebView alloc] initWithFrame:rect frameName:nil groupName:@"org.webkit.DumpRenderTree"]; + + [webView setUIDelegate:uiDelegate]; + [webView setFrameLoadDelegate:frameLoadDelegate]; + [webView setEditingDelegate:editingDelegate]; + [webView setResourceLoadDelegate:resourceLoadDelegate]; + + // Register the same schemes that Safari does + [WebView registerURLSchemeAsLocal:@"feed"]; + [WebView registerURLSchemeAsLocal:@"feeds"]; + [WebView registerURLSchemeAsLocal:@"feedsearch"]; + + [webView setContinuousSpellCheckingEnabled:YES]; + + // To make things like certain NSViews, dragging, and plug-ins work, put the WebView a window, but put it off-screen so you don't see it. + // Put it at -10000, -10000 in "flipped coordinates", since WebCore and the DOM use flipped coordinates. + NSRect windowRect = NSOffsetRect(rect, -10000, [[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.size.height + 10000); + DumpRenderTreeWindow *window = [[DumpRenderTreeWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]; + [[window contentView] addSubview:webView]; + [window orderBack:nil]; + [window setAutodisplay:NO]; + + [window startObservingWebView]; + + // For reasons that are not entirely clear, the following pair of calls makes WebView handle its + // dynamic scrollbars properly. Without it, every frame will always have scrollbars. + NSBitmapImageRep *imageRep = [webView bitmapImageRepForCachingDisplayInRect:[webView bounds]]; + [webView cacheDisplayInRect:[webView bounds] toBitmapImageRep:imageRep]; + + return webView; +} + +void testStringByEvaluatingJavaScriptFromString() +{ + // maps expected result <= JavaScript expression + NSDictionary *expressions = [NSDictionary dictionaryWithObjectsAndKeys: + @"0", @"0", + @"0", @"'0'", + @"", @"", + @"", @"''", + @"", @"new String()", + @"", @"new String('0')", + @"", @"throw 1", + @"", @"{ }", + @"", @"[ ]", + @"", @"//", + @"", @"a.b.c", + @"", @"(function() { throw 'error'; })()", + @"", @"null", + @"", @"undefined", + @"true", @"true", + @"false", @"false", + nil + ]; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + WebView *webView = [[WebView alloc] initWithFrame:NSZeroRect frameName:@"" groupName:@""]; + + NSEnumerator *enumerator = [expressions keyEnumerator]; + id expression; + while ((expression = [enumerator nextObject])) { + NSString *expectedResult = [expressions objectForKey:expression]; + NSString *result = [webView stringByEvaluatingJavaScriptFromString:expression]; + assert([result isEqualToString:expectedResult]); + } + + [webView close]; + [webView release]; + [pool release]; +} + +static void setDefaultsToConsistentValuesForTesting() +{ + // Give some clear to undocumented defaults values + static const int NoFontSmoothing = 0; + static const int BlueTintedAppearance = 1; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setInteger:4 forKey:@"AppleAntiAliasingThreshold"]; // smallest font size to CG should perform antialiasing on + [defaults setInteger:NoFontSmoothing forKey:@"AppleFontSmoothing"]; + [defaults setInteger:BlueTintedAppearance forKey:@"AppleAquaColorVariant"]; + [defaults setObject:@"0.709800 0.835300 1.000000" forKey:@"AppleHighlightColor"]; + [defaults setObject:@"0.500000 0.500000 0.500000" forKey:@"AppleOtherHighlightColor"]; + [defaults setObject:[NSArray arrayWithObject:@"en"] forKey:@"AppleLanguages"]; + + // Scrollbars are drawn either using AppKit (which uses NSUserDefaults) or using HIToolbox (which uses CFPreferences / kCFPreferencesAnyApplication / kCFPreferencesCurrentUser / kCFPreferencesAnyHost) + [defaults setObject:@"DoubleMax" forKey:@"AppleScrollBarVariant"]; + RetainPtr<CFTypeRef> initialValue = CFPreferencesCopyValue(CFSTR("AppleScrollBarVariant"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + CFPreferencesSetValue(CFSTR("AppleScrollBarVariant"), CFSTR("DoubleMax"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); +#ifndef __LP64__ + // See <rdar://problem/6347388>. + ThemeScrollBarArrowStyle style; + GetThemeScrollBarArrowStyle(&style); // Force HIToolbox to read from CFPreferences +#endif + if (initialValue) + CFPreferencesSetValue(CFSTR("AppleScrollBarVariant"), initialValue.get(), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + + NSString *libraryPath = [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath]; + [defaults setObject:[libraryPath stringByAppendingPathComponent:@"Databases"] forKey:WebDatabaseDirectoryDefaultsKey]; + + WebPreferences *preferences = [WebPreferences standardPreferences]; + + [preferences setStandardFontFamily:@"Times"]; + [preferences setFixedFontFamily:@"Courier"]; + [preferences setSerifFontFamily:@"Times"]; + [preferences setSansSerifFontFamily:@"Helvetica"]; + [preferences setCursiveFontFamily:@"Apple Chancery"]; + [preferences setFantasyFontFamily:@"Papyrus"]; + [preferences setDefaultFontSize:16]; + [preferences setDefaultFixedFontSize:13]; + [preferences setMinimumFontSize:1]; + [preferences setJavaEnabled:NO]; + [preferences setJavaScriptEnabled:YES]; + [preferences setEditableLinkBehavior:WebKitEditableLinkOnlyLiveWithShiftKey]; + [preferences setTabsToLinks:NO]; + [preferences setDOMPasteAllowed:YES]; + [preferences setFullDocumentTeardownEnabled:YES]; + [preferences setShouldPrintBackgrounds:YES]; + + // The back/forward cache is causing problems due to layouts during transition from one page to another. + // So, turn it off for now, but we might want to turn it back on some day. + [preferences setUsesPageCache:NO]; +} + +static void crashHandler(int sig) +{ + char *signalName = strsignal(sig); + write(STDERR_FILENO, signalName, strlen(signalName)); + write(STDERR_FILENO, "\n", 1); + restoreMainDisplayColorProfile(0); + exit(128 + sig); +} + +static void installSignalHandlers() +{ + signal(SIGILL, crashHandler); /* 4: illegal instruction (not reset when caught) */ + signal(SIGTRAP, crashHandler); /* 5: trace trap (not reset when caught) */ + signal(SIGEMT, crashHandler); /* 7: EMT instruction */ + signal(SIGFPE, crashHandler); /* 8: floating point exception */ + signal(SIGBUS, crashHandler); /* 10: bus error */ + signal(SIGSEGV, crashHandler); /* 11: segmentation violation */ + signal(SIGSYS, crashHandler); /* 12: bad argument to system call */ + signal(SIGPIPE, crashHandler); /* 13: write on a pipe with no reader */ + signal(SIGXCPU, crashHandler); /* 24: exceeded CPU time limit */ + signal(SIGXFSZ, crashHandler); /* 25: exceeded file size limit */ +} + +static void allocateGlobalControllers() +{ + // FIXME: We should remove these and move to the ObjC standard [Foo sharedInstance] model + gNavigationController = [[NavigationController alloc] init]; + frameLoadDelegate = [[FrameLoadDelegate alloc] init]; + uiDelegate = [[UIDelegate alloc] init]; + editingDelegate = [[EditingDelegate alloc] init]; + resourceLoadDelegate = [[ResourceLoadDelegate alloc] init]; + policyDelegate = [[PolicyDelegate alloc] init]; +} + +// ObjC++ doens't seem to let me pass NSObject*& sadly. +static inline void releaseAndZero(NSObject** object) +{ + [*object release]; + *object = nil; +} + +static void releaseGlobalControllers() +{ + releaseAndZero(&gNavigationController); + releaseAndZero(&frameLoadDelegate); + releaseAndZero(&editingDelegate); + releaseAndZero(&resourceLoadDelegate); + releaseAndZero(&uiDelegate); + releaseAndZero(&policyDelegate); +} + +static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[]) +{ + struct option options[] = { + {"notree", no_argument, &dumpTree, NO}, + {"pixel-tests", no_argument, &dumpPixels, YES}, + {"tree", no_argument, &dumpTree, YES}, + {"threaded", no_argument, &threaded, YES}, + {"complex-text", no_argument, &forceComplexText, YES}, + {NULL, 0, NULL, 0} + }; + + int option; + while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) { + switch (option) { + case '?': // unknown or ambiguous option + case ':': // missing argument + exit(1); + break; + } + } +} + +static void addTestPluginsToPluginSearchPath(const char* executablePath) +{ + NSString *pwd = [[NSString stringWithUTF8String:executablePath] stringByDeletingLastPathComponent]; + [WebPluginDatabase setAdditionalWebPlugInPaths:[NSArray arrayWithObject:pwd]]; + [[WebPluginDatabase sharedDatabase] refresh]; +} + +static bool useLongRunningServerMode(int argc, const char *argv[]) +{ + // This assumes you've already called getopt_long + return (argc == optind+1 && strcmp(argv[optind], "-") == 0); +} + +static void runTestingServerLoop() +{ + // When DumpRenderTree run in server mode, we just wait around for file names + // to be passed to us and read each in turn, passing the results back to the client + char filenameBuffer[2048]; + while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) { + char *newLineCharacter = strchr(filenameBuffer, '\n'); + if (newLineCharacter) + *newLineCharacter = '\0'; + + if (strlen(filenameBuffer) == 0) + continue; + + runTest(filenameBuffer); + } +} + +static void prepareConsistentTestingEnvironment() +{ + poseAsClass("DumpRenderTreePasteboard", "NSPasteboard"); + poseAsClass("DumpRenderTreeEvent", "NSEvent"); + + setDefaultsToConsistentValuesForTesting(); + activateFonts(); + + if (dumpPixels) + setupMainDisplayColorProfile(); + allocateGlobalControllers(); + + makeLargeMallocFailSilently(); +} + +void dumpRenderTree(int argc, const char *argv[]) +{ + initializeGlobalsFromCommandLineOptions(argc, argv); + prepareConsistentTestingEnvironment(); + addTestPluginsToPluginSearchPath(argv[0]); + if (dumpPixels) + installSignalHandlers(); + + if (forceComplexText) + [WebView _setAlwaysUsesComplexTextCodePath:YES]; + + WebView *webView = createWebViewAndOffscreenWindow(); + mainFrame = [webView mainFrame]; + + [[NSURLCache sharedURLCache] removeAllCachedResponses]; + + [WebCache empty]; + + // <rdar://problem/5222911> + testStringByEvaluatingJavaScriptFromString(); + + if (threaded) + startJavaScriptThreads(); + + if (useLongRunningServerMode(argc, argv)) { + printSeparators = YES; + runTestingServerLoop(); + } else { + printSeparators = (optind < argc-1 || (dumpPixels && dumpTree)); + for (int i = optind; i != argc; ++i) + runTest(argv[i]); + } + + if (threaded) + stopJavaScriptThreads(); + + NSWindow *window = [webView window]; + [webView close]; + mainFrame = nil; + + // Work around problem where registering drag types leaves an outstanding + // "perform selector" on the window, which retains the window. It's a bit + // inelegant and perhaps dangerous to just blow them all away, but in practice + // it probably won't cause any trouble (and this is just a test tool, after all). + [NSObject cancelPreviousPerformRequestsWithTarget:window]; + + [window close]; // releases when closed + [webView release]; + + releaseGlobalControllers(); + + [DumpRenderTreePasteboard releaseLocalPasteboards]; + + // FIXME: This should be moved onto LayoutTestController and made into a HashSet + if (disallowedURLs) { + CFRelease(disallowedURLs); + disallowedURLs = 0; + } + + if (dumpPixels) + restoreMainDisplayColorProfile(0); +} + +int main(int argc, const char *argv[]) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; // Force AppKit to init itself + dumpRenderTree(argc, argv); + [WebCoreStatistics garbageCollectJavaScriptObjects]; + [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts + [pool release]; + return 0; +} + +static NSInteger compareHistoryItems(id item1, id item2, void *context) +{ + return [[item1 target] caseInsensitiveCompare:[item2 target]]; +} + +static void dumpHistoryItem(WebHistoryItem *item, int indent, BOOL current) +{ + int start = 0; + if (current) { + printf("curr->"); + start = 6; + } + for (int i = start; i < indent; i++) + putchar(' '); + printf("%s", [[item URLString] UTF8String]); + NSString *target = [item target]; + if (target && [target length] > 0) + printf(" (in frame \"%s\")", [target UTF8String]); + if ([item isTargetItem]) + printf(" **nav target**"); + putchar('\n'); + NSArray *kids = [item children]; + if (kids) { + // must sort to eliminate arbitrary result ordering which defeats reproducible testing + kids = [kids sortedArrayUsingFunction:&compareHistoryItems context:nil]; + for (unsigned i = 0; i < [kids count]; i++) + dumpHistoryItem([kids objectAtIndex:i], indent+4, NO); + } +} + +static void dumpFrameScrollPosition(WebFrame *f) +{ + NSPoint scrollPosition = [[[[f frameView] documentView] superview] bounds].origin; + if (ABS(scrollPosition.x) > 0.00000001 || ABS(scrollPosition.y) > 0.00000001) { + if ([f parentFrame] != nil) + printf("frame '%s' ", [[f name] UTF8String]); + printf("scrolled to %.f,%.f\n", scrollPosition.x, scrollPosition.y); + } + + if (gLayoutTestController->dumpChildFrameScrollPositions()) { + NSArray *kids = [f childFrames]; + if (kids) + for (unsigned i = 0; i < [kids count]; i++) + dumpFrameScrollPosition([kids objectAtIndex:i]); + } +} + +static NSString *dumpFramesAsText(WebFrame *frame) +{ + DOMDocument *document = [frame DOMDocument]; + DOMElement *documentElement = [document documentElement]; + + if (!documentElement) + return @""; + + NSMutableString *result = [[[NSMutableString alloc] init] autorelease]; + + // Add header for all but the main frame. + if ([frame parentFrame]) + result = [NSMutableString stringWithFormat:@"\n--------\nFrame: '%@'\n--------\n", [frame name]]; + + [result appendFormat:@"%@\n", [documentElement innerText]]; + + if (gLayoutTestController->dumpChildFramesAsText()) { + NSArray *kids = [frame childFrames]; + if (kids) { + for (unsigned i = 0; i < [kids count]; i++) + [result appendString:dumpFramesAsText([kids objectAtIndex:i])]; + } + } + + return result; +} + +static NSData *dumpFrameAsPDF(WebFrame *frame) +{ + if (!frame) + return nil; + + // Sadly we have to dump to a file and then read from that file again + // +[NSPrintOperation PDFOperationWithView:insideRect:] requires a rect and prints to a single page + // likewise +[NSView dataWithPDFInsideRect:] also prints to a single continuous page + // The goal of this function is to test "real" printing across multiple pages. + // FIXME: It's possible there might be printing SPI to let us print a multi-page PDF to an NSData object + NSString *path = @"/tmp/test.pdf"; + + NSMutableDictionary *printInfoDict = [NSMutableDictionary dictionaryWithDictionary:[[NSPrintInfo sharedPrintInfo] dictionary]]; + [printInfoDict setObject:NSPrintSaveJob forKey:NSPrintJobDisposition]; + [printInfoDict setObject:path forKey:NSPrintSavePath]; + + NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict]; + [printInfo setHorizontalPagination:NSAutoPagination]; + [printInfo setVerticalPagination:NSAutoPagination]; + [printInfo setVerticallyCentered:NO]; + + NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:[frame frameView] printInfo:printInfo]; + [printOperation setShowPanels:NO]; + [printOperation runOperation]; + + [printInfo release]; + + NSData *pdfData = [NSData dataWithContentsOfFile:path]; + [[NSFileManager defaultManager] removeFileAtPath:path handler:nil]; + + return pdfData; +} + +static void convertMIMEType(NSMutableString *mimeType) +{ +#ifdef BUILDING_ON_LEOPARD + // Workaround for <rdar://problem/5539824> on Leopard + if ([mimeType isEqualToString:@"text/xml"]) + [mimeType setString:@"application/xml"]; +#endif + // Workaround for <rdar://problem/6234318> with Dashcode 2.0 + if ([mimeType isEqualToString:@"application/x-javascript"]) + [mimeType setString:@"text/javascript"]; +} + +static void convertWebResourceDataToString(NSMutableDictionary *resource) +{ + NSMutableString *mimeType = [resource objectForKey:@"WebResourceMIMEType"]; + convertMIMEType(mimeType); + + if ([mimeType hasPrefix:@"text/"] || [[WebHTMLRepresentation supportedNonImageMIMETypes] containsObject:mimeType]) { + NSString *textEncodingName = [resource objectForKey:@"WebResourceTextEncodingName"]; + NSStringEncoding stringEncoding; + if ([textEncodingName length] > 0) + stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)textEncodingName)); + else + stringEncoding = NSUTF8StringEncoding; + + NSData *data = [resource objectForKey:@"WebResourceData"]; + NSString *dataAsString = [[NSString alloc] initWithData:data encoding:stringEncoding]; + if (dataAsString) + [resource setObject:dataAsString forKey:@"WebResourceData"]; + [dataAsString release]; + } +} + +static void normalizeHTTPResponseHeaderFields(NSMutableDictionary *fields) +{ + // Normalize headers + if ([fields objectForKey:@"Date"]) + [fields setObject:@"Sun, 16 Nov 2008 17:00:00 GMT" forKey:@"Date"]; + if ([fields objectForKey:@"Last-Modified"]) + [fields setObject:@"Sun, 16 Nov 2008 16:55:00 GMT" forKey:@"Last-Modified"]; + if ([fields objectForKey:@"Etag"]) + [fields setObject:@"\"301925-21-45c7d72d3e780\"" forKey:@"Etag"]; + if ([fields objectForKey:@"Server"]) + [fields setObject:@"Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l PHP/5.2.6" forKey:@"Server"]; + + // Remove headers + if ([fields objectForKey:@"Connection"]) + [fields removeObjectForKey:@"Connection"]; + if ([fields objectForKey:@"Keep-Alive"]) + [fields removeObjectForKey:@"Keep-Alive"]; +} + +static void normalizeWebResourceURL(NSMutableString *webResourceURL) +{ + static int fileUrlLength = [(NSString *)@"file://" length]; + NSRange layoutTestsWebArchivePathRange = [webResourceURL rangeOfString:@"/LayoutTests/" options:NSBackwardsSearch]; + if (layoutTestsWebArchivePathRange.location == NSNotFound) + return; + NSRange currentWorkingDirectoryRange = NSMakeRange(fileUrlLength, layoutTestsWebArchivePathRange.location - fileUrlLength); + [webResourceURL replaceCharactersInRange:currentWorkingDirectoryRange withString:@""]; +} + +static void convertWebResourceResponseToDictionary(NSMutableDictionary *propertyList) +{ + NSURLResponse *response = nil; + NSData *responseData = [propertyList objectForKey:@"WebResourceResponse"]; // WebResourceResponseKey in WebResource.m + if ([responseData isKindOfClass:[NSData class]]) { + // Decode NSURLResponse + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:responseData]; + response = [unarchiver decodeObjectForKey:@"WebResourceResponse"]; // WebResourceResponseKey in WebResource.m + [unarchiver finishDecoding]; + [unarchiver release]; + } + + NSMutableDictionary *responseDictionary = [[NSMutableDictionary alloc] init]; + + NSMutableString *urlString = [[[response URL] description] mutableCopy]; + normalizeWebResourceURL(urlString); + [responseDictionary setObject:urlString forKey:@"URL"]; + [urlString release]; + + NSMutableString *mimeTypeString = [[response MIMEType] mutableCopy]; + convertMIMEType(mimeTypeString); + [responseDictionary setObject:mimeTypeString forKey:@"MIMEType"]; + [mimeTypeString release]; + + NSString *textEncodingName = [response textEncodingName]; + if (textEncodingName) + [responseDictionary setObject:textEncodingName forKey:@"textEncodingName"]; + [responseDictionary setObject:[NSNumber numberWithLongLong:[response expectedContentLength]] forKey:@"expectedContentLength"]; + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + NSMutableDictionary *allHeaderFields = [[httpResponse allHeaderFields] mutableCopy]; + normalizeHTTPResponseHeaderFields(allHeaderFields); + [responseDictionary setObject:allHeaderFields forKey:@"allHeaderFields"]; + [allHeaderFields release]; + + [responseDictionary setObject:[NSNumber numberWithInt:[httpResponse statusCode]] forKey:@"statusCode"]; + } + + [propertyList setObject:responseDictionary forKey:@"WebResourceResponse"]; + [responseDictionary release]; +} + +static NSInteger compareResourceURLs(id resource1, id resource2, void *context) +{ + NSString *url1 = [resource1 objectForKey:@"WebResourceURL"]; + NSString *url2 = [resource2 objectForKey:@"WebResourceURL"]; + + return [url1 compare:url2]; +} + +static NSString *serializeWebArchiveToXML(WebArchive *webArchive) +{ + NSString *errorString; + NSMutableDictionary *propertyList = [NSPropertyListSerialization propertyListFromData:[webArchive data] + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:NULL + errorDescription:&errorString]; + if (!propertyList) + return errorString; + + NSMutableArray *resources = [NSMutableArray arrayWithCapacity:1]; + [resources addObject:propertyList]; + + while ([resources count]) { + NSMutableDictionary *resourcePropertyList = [resources objectAtIndex:0]; + [resources removeObjectAtIndex:0]; + + NSMutableDictionary *mainResource = [resourcePropertyList objectForKey:@"WebMainResource"]; + normalizeWebResourceURL([mainResource objectForKey:@"WebResourceURL"]); + convertWebResourceDataToString(mainResource); + + // Add subframeArchives to list for processing + NSMutableArray *subframeArchives = [resourcePropertyList objectForKey:@"WebSubframeArchives"]; // WebSubframeArchivesKey in WebArchive.m + if (subframeArchives) + [resources addObjectsFromArray:subframeArchives]; + + NSMutableArray *subresources = [resourcePropertyList objectForKey:@"WebSubresources"]; // WebSubresourcesKey in WebArchive.m + NSEnumerator *enumerator = [subresources objectEnumerator]; + NSMutableDictionary *subresourcePropertyList; + while ((subresourcePropertyList = [enumerator nextObject])) { + normalizeWebResourceURL([subresourcePropertyList objectForKey:@"WebResourceURL"]); + convertWebResourceResponseToDictionary(subresourcePropertyList); + convertWebResourceDataToString(subresourcePropertyList); + } + + // Sort the subresources so they're always in a predictable order for the dump + if (NSArray *sortedSubresources = [subresources sortedArrayUsingFunction:compareResourceURLs context:nil]) + [resourcePropertyList setObject:sortedSubresources forKey:@"WebSubresources"]; + } + + NSData *xmlData = [NSPropertyListSerialization dataFromPropertyList:propertyList + format:NSPropertyListXMLFormat_v1_0 + errorDescription:&errorString]; + if (!xmlData) + return errorString; + + NSMutableString *string = [[[NSMutableString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding] autorelease]; + + // Replace "Apple Computer" with "Apple" in the DTD declaration. + NSRange range = [string rangeOfString:@"-//Apple Computer//"]; + if (range.location != NSNotFound) + [string replaceCharactersInRange:range withString:@"-//Apple//"]; + + return string; +} + +static void dumpBackForwardListForWebView(WebView *view) +{ + printf("\n============== Back Forward List ==============\n"); + WebBackForwardList *bfList = [view backForwardList]; + + // Print out all items in the list after prevTestBFItem, which was from the previous test + // Gather items from the end of the list, the print them out from oldest to newest + NSMutableArray *itemsToPrint = [[NSMutableArray alloc] init]; + for (int i = [bfList forwardListCount]; i > 0; i--) { + WebHistoryItem *item = [bfList itemAtIndex:i]; + // something is wrong if the item from the last test is in the forward part of the b/f list + assert(item != prevTestBFItem); + [itemsToPrint addObject:item]; + } + + assert([bfList currentItem] != prevTestBFItem); + [itemsToPrint addObject:[bfList currentItem]]; + int currentItemIndex = [itemsToPrint count] - 1; + + for (int i = -1; i >= -[bfList backListCount]; i--) { + WebHistoryItem *item = [bfList itemAtIndex:i]; + if (item == prevTestBFItem) + break; + [itemsToPrint addObject:item]; + } + + for (int i = [itemsToPrint count]-1; i >= 0; i--) + dumpHistoryItem([itemsToPrint objectAtIndex:i], 8, i == currentItemIndex); + + [itemsToPrint release]; + printf("===============================================\n"); +} + +static void sizeWebViewForCurrentTest() +{ + // W3C SVG tests expect to be 480x360 + bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos); + if (isSVGW3CTest) + [[mainFrame webView] setFrameSize:NSMakeSize(480, 360)]; + else + [[mainFrame webView] setFrameSize:NSMakeSize(maxViewWidth, maxViewHeight)]; +} + +static const char *methodNameStringForFailedTest() +{ + const char *errorMessage; + if (gLayoutTestController->dumpAsText()) + errorMessage = "[documentElement innerText]"; + else if (gLayoutTestController->dumpDOMAsWebArchive()) + errorMessage = "[[mainFrame DOMDocument] webArchive]"; + else if (gLayoutTestController->dumpSourceAsWebArchive()) + errorMessage = "[[mainFrame dataSource] webArchive]"; + else + errorMessage = "[mainFrame renderTreeAsExternalRepresentation]"; + + return errorMessage; +} + +static void dumpBackForwardListForAllWindows() +{ + CFArrayRef openWindows = (CFArrayRef)[DumpRenderTreeWindow openWindows]; + unsigned count = CFArrayGetCount(openWindows); + for (unsigned i = 0; i < count; i++) { + NSWindow *window = (NSWindow *)CFArrayGetValueAtIndex(openWindows, i); + WebView *webView = [[[window contentView] subviews] objectAtIndex:0]; + dumpBackForwardListForWebView(webView); + } +} + +static void invalidateAnyPreviousWaitToDumpWatchdog() +{ + if (waitToDumpWatchdog) { + CFRunLoopTimerInvalidate(waitToDumpWatchdog); + CFRelease(waitToDumpWatchdog); + waitToDumpWatchdog = 0; + } +} + +void dump() +{ + invalidateAnyPreviousWaitToDumpWatchdog(); + + bool dumpAsText = gLayoutTestController->dumpAsText(); + if (dumpTree) { + NSString *resultString = nil; + NSData *resultData = nil; + NSString *resultMimeType = @"text/plain"; + + dumpAsText |= [[[mainFrame dataSource] _responseMIMEType] isEqualToString:@"text/plain"]; + gLayoutTestController->setDumpAsText(dumpAsText); + if (gLayoutTestController->dumpAsText()) { + resultString = dumpFramesAsText(mainFrame); + } else if (gLayoutTestController->dumpAsPDF()) { + resultData = dumpFrameAsPDF(mainFrame); + resultMimeType = @"application/pdf"; + } else if (gLayoutTestController->dumpDOMAsWebArchive()) { + WebArchive *webArchive = [[mainFrame DOMDocument] webArchive]; + resultString = serializeWebArchiveToXML(webArchive); + resultMimeType = @"application/x-webarchive"; + } else if (gLayoutTestController->dumpSourceAsWebArchive()) { + WebArchive *webArchive = [[mainFrame dataSource] webArchive]; + resultString = serializeWebArchiveToXML(webArchive); + resultMimeType = @"application/x-webarchive"; + } else { + sizeWebViewForCurrentTest(); + resultString = [mainFrame renderTreeAsExternalRepresentation]; + } + + if (resultString && !resultData) + resultData = [resultString dataUsingEncoding:NSUTF8StringEncoding]; + + printf("Content-Type: %s\n", [resultMimeType UTF8String]); + + if (resultData) { + fwrite([resultData bytes], 1, [resultData length], stdout); + + if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive()) + dumpFrameScrollPosition(mainFrame); + + if (gLayoutTestController->dumpBackForwardList()) + dumpBackForwardListForAllWindows(); + } else + printf("ERROR: nil result from %s", methodNameStringForFailedTest()); + + 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); + fflush(stderr); + + done = YES; +} + +static bool shouldLogFrameLoadDelegates(const char *pathOrURL) +{ + return strstr(pathOrURL, "loading/"); +} + +static void resetWebViewToConsistentStateBeforeTesting() +{ + WebView *webView = [mainFrame webView]; + [(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES]; + [webView makeTextStandardSize:nil]; + [webView resetPageZoom:nil]; + [webView setTabKeyCyclesThroughElements:YES]; + [webView setPolicyDelegate:nil]; + [webView _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO]; + [webView _clearMainFrameName]; + [[webView undoManager] removeAllActions]; + + WebPreferences *preferences = [webView preferences]; + [preferences setPrivateBrowsingEnabled:NO]; + [preferences setAuthorAndUserStylesEnabled:YES]; + [preferences setJavaScriptCanOpenWindowsAutomatically:YES]; + [preferences setOfflineWebApplicationCacheEnabled:YES]; + [preferences setFullDocumentTeardownEnabled:YES]; + [preferences setDeveloperExtrasEnabled:NO]; + + if (persistentUserStyleSheetLocation) { + [preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]]; + [preferences setUserStyleSheetEnabled:YES]; + } else + [preferences setUserStyleSheetEnabled:NO]; + + [[mainFrame webView] setSmartInsertDeleteEnabled:YES]; + [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:NO]; + + [WebView _setUsesTestModeFocusRingColor:YES]; +} + +static void runTest(const string& testPathOrURL) +{ + ASSERT(!testPathOrURL.empty()); + + // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows. + string pathOrURL(testPathOrURL); + string expectedPixelHash; + + size_t separatorPos = pathOrURL.find("'"); + if (separatorPos != string::npos) { + pathOrURL = string(testPathOrURL, 0, separatorPos); + expectedPixelHash = string(testPathOrURL, separatorPos + 1); + } + + NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()]; + if (!pathOrURLString) { + fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str()); + return; + } + + NSURL *url; + if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"]) + url = [NSURL URLWithString:pathOrURLString]; + else + url = [NSURL fileURLWithPath:pathOrURLString]; + if (!url) { + fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str()); + return; + } + + const string testURL([[url absoluteString] UTF8String]); + + resetWebViewToConsistentStateBeforeTesting(); + + gLayoutTestController = new LayoutTestController(testURL, expectedPixelHash); + topLoadingFrame = nil; + done = NO; + + gLayoutTestController->setIconDatabaseEnabled(false); + + if (disallowedURLs) + CFSetRemoveAllValues(disallowedURLs); + if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) + gLayoutTestController->setDumpFrameLoadCallbacks(true); + + if ([WebHistory optionalSharedHistory]) + [WebHistory setOptionalSharedHistory:nil]; + lastMousePosition = NSZeroPoint; + lastClickPosition = NSZeroPoint; + + [prevTestBFItem release]; + prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain]; + + WorkQueue::shared()->clear(); + WorkQueue::shared()->setFrozen(false); + + bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL); + if (ignoreWebCoreNodeLeaks) + [WebCoreStatistics startIgnoringWebCoreNodeLeaks]; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [mainFrame loadRequest:[NSURLRequest requestWithURL:url]]; + [pool release]; + while (!done) { + pool = [[NSAutoreleasePool alloc] init]; + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]; + [pool release]; + } + pool = [[NSAutoreleasePool alloc] init]; + [EventSendingController clearSavedEvents]; + [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream]; + + WorkQueue::shared()->clear(); + + if (gLayoutTestController->closeRemainingWindowsWhenComplete()) { + NSArray* array = [DumpRenderTreeWindow openWindows]; + + unsigned count = [array count]; + for (unsigned i = 0; i < count; i++) { + NSWindow *window = [array objectAtIndex:i]; + + // Don't try to close the main window + if (window == [[mainFrame webView] window]) + continue; + + WebView *webView = [[[window contentView] subviews] objectAtIndex:0]; + + [webView close]; + [window close]; + } + } + + [mainFrame loadHTMLString:@"<html></html>" baseURL:[NSURL URLWithString:@"about:blank"]]; + [mainFrame stopLoading]; + + [pool release]; + + // We should only have our main window left open when we're done + ASSERT(CFArrayGetCount(openWindowsRef) == 1); + ASSERT(CFArrayGetValueAtIndex(openWindowsRef, 0) == [[mainFrame webView] window]); + + gLayoutTestController->deref(); + gLayoutTestController = 0; + + if (ignoreWebCoreNodeLeaks) + [WebCoreStatistics stopIgnoringWebCoreNodeLeaks]; +} + +void displayWebView() +{ + NSView *webView = [mainFrame webView]; + [webView display]; + [webView lockFocus]; + [[[NSColor blackColor] colorWithAlphaComponent:0.66] set]; + NSRectFillUsingOperation([webView frame], NSCompositeSourceOver); + [webView unlockFocus]; +} + +@implementation DumpRenderTreeEvent + ++ (NSPoint)mouseLocation +{ + return [[[mainFrame webView] window] convertBaseToScreen:lastMousePosition]; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeDraggingInfo.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeDraggingInfo.h new file mode 100644 index 0000000..249809c --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeDraggingInfo.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +@interface DumpRenderTreeDraggingInfo : NSObject <NSDraggingInfo> { +@private + NSSize offset; + NSImage *draggedImage; + NSPasteboard *draggingPasteboard; + id draggingSource; +} + +- (id)initWithImage:(NSImage *)image offset:(NSSize)offset pasteboard:(NSPasteboard *)pasteboard source:(id)source; + +- (NSWindow *)draggingDestinationWindow; +- (NSDragOperation)draggingSourceOperationMask; +- (NSPoint)draggingLocation; +- (NSPoint)draggedImageLocation; +- (NSImage *)draggedImage; +- (NSPasteboard *)draggingPasteboard; +- (id)draggingSource; +- (int)draggingSequenceNumber; + +- (void)slideDraggedImageTo:(NSPoint)screenPoint; +- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination; +@end + diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeDraggingInfo.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeDraggingInfo.mm new file mode 100644 index 0000000..41aa639 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeDraggingInfo.mm @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTreeDraggingInfo.h" + +#import "DumpRenderTree.h" +#import "EventSendingController.h" +#import <WebKit/WebKit.h> + +@implementation DumpRenderTreeDraggingInfo + +- (id)initWithImage:(NSImage *)anImage offset:(NSSize)o pasteboard:(NSPasteboard *)pboard source:(id)source +{ + draggedImage = [anImage retain]; + draggingPasteboard = [pboard retain]; + draggingSource = [source retain]; + offset = o; + + return [super init]; +} + +- (void)dealloc +{ + [draggedImage release]; + [draggingPasteboard release]; + [draggingSource release]; + [super dealloc]; +} + +- (NSWindow *)draggingDestinationWindow +{ + return [[mainFrame webView] window]; +} + +- (NSDragOperation)draggingSourceOperationMask +{ + return [draggingSource draggingSourceOperationMaskForLocal:YES]; +} + +- (NSPoint)draggingLocation +{ + return lastMousePosition; +} + +- (NSPoint)draggedImageLocation +{ + return NSMakePoint(lastMousePosition.x + offset.width, lastMousePosition.y + offset.height); +} + +- (NSImage *)draggedImage +{ + return draggedImage; +} + +- (NSPasteboard *)draggingPasteboard +{ + return draggingPasteboard; +} + +- (id)draggingSource +{ + return draggingSource; +} + +- (int)draggingSequenceNumber +{ + NSLog(@"DumpRenderTree doesn't support draggingSequenceNumber"); + return 0; +} + +- (void)slideDraggedImageTo:(NSPoint)screenPoint +{ + NSLog(@"DumpRenderTree doesn't support slideDraggedImageTo:"); +} + +- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination +{ + NSLog(@"DumpRenderTree doesn't support namesOfPromisedFilesDroppedAtDestination:"); + return nil; +} + +@end + diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h new file mode 100644 index 0000000..72d5db1 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DumpRenderTreeMac_h +#define DumpRenderTreeMac_h + +// FIXME: we should add a config.h file for DumpRenderTree. +#define WTF_PLATFORM_CF 1 + +#if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +#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 +#endif + +@class DumpRenderTreeDraggingInfo; +@class NavigationController; +@class PolicyDelegate; +@class WebFrame; +@class WebView; + +typedef const struct __CFString* CFStringRef; + +extern CFMutableArrayRef openWindowsRef; +extern CFMutableSetRef disallowedURLs; +extern WebFrame* mainFrame; +extern WebFrame* topLoadingFrame; +extern DumpRenderTreeDraggingInfo *draggingInfo; +extern NavigationController* gNavigationController; +extern PolicyDelegate* policyDelegate; + +extern const unsigned maxViewHeight; +extern const unsigned maxViewWidth; + +extern CFRunLoopTimerRef waitToDumpWatchdog; + +WebView* createWebViewAndOffscreenWindow(); +void setPersistentUserStyleSheetLocation(CFStringRef); + +#endif // DumpRenderTreeMac_h diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreePasteboard.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreePasteboard.h new file mode 100644 index 0000000..ba2754b --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreePasteboard.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <AppKit/AppKit.h> +#import <WebKit/WebTypesInternal.h> + +@interface DumpRenderTreePasteboard : NSPasteboard +- (NSInteger)declareType:(NSString *)type owner:(id)newOwner; ++ (void)releaseLocalPasteboards; +@end + diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreePasteboard.m b/WebKitTools/DumpRenderTree/mac/DumpRenderTreePasteboard.m new file mode 100644 index 0000000..a797b5c --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreePasteboard.m @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTreeMac.h" +#import "DumpRenderTreePasteboard.h" + +#import <WebKit/WebTypesInternal.h> + +@interface LocalPasteboard : NSPasteboard +{ + NSMutableArray *typesArray; + NSMutableSet *typesSet; + NSMutableDictionary *dataByType; + NSInteger changeCount; +} +@end + +static NSMutableDictionary *localPasteboards; + +@implementation DumpRenderTreePasteboard + +// Return a local pasteboard so we don't disturb the real pasteboards when running tests. ++ (NSPasteboard *)_pasteboardWithName:(NSString *)name +{ + static int number = 0; + if (!name) + name = [NSString stringWithFormat:@"LocalPasteboard%d", ++number]; + if (!localPasteboards) + localPasteboards = [[NSMutableDictionary alloc] init]; + LocalPasteboard *pasteboard = [localPasteboards objectForKey:name]; + if (pasteboard) + return pasteboard; + pasteboard = [[LocalPasteboard alloc] init]; + [localPasteboards setObject:pasteboard forKey:name]; + [pasteboard release]; + return pasteboard; +} + ++ (void)releaseLocalPasteboards +{ + [localPasteboards release]; + localPasteboards = nil; +} + +// Convenience method for JS so that it doesn't have to try and create a NSArray on the objc side instead +// of the usual WebScriptObject that is passed around +- (NSInteger)declareType:(NSString *)type owner:(id)newOwner +{ + return [self declareTypes:[NSArray arrayWithObject:type] owner:newOwner]; +} + +@end + +@implementation LocalPasteboard + ++ (id)alloc +{ + return NSAllocateObject(self, 0, 0); +} + +- (id)init +{ + typesArray = [[NSMutableArray alloc] init]; + typesSet = [[NSMutableSet alloc] init]; + dataByType = [[NSMutableDictionary alloc] init]; + return self; +} + +- (void)dealloc +{ + [typesArray release]; + [typesSet release]; + [dataByType release]; + [super dealloc]; +} + +- (NSString *)name +{ + return nil; +} + +- (void)releaseGlobally +{ +} + +- (NSInteger)declareTypes:(NSArray *)newTypes owner:(id)newOwner +{ + [typesArray removeAllObjects]; + [typesSet removeAllObjects]; + [dataByType removeAllObjects]; + return [self addTypes:newTypes owner:newOwner]; +} + +- (NSInteger)addTypes:(NSArray *)newTypes owner:(id)newOwner +{ + unsigned count = [newTypes count]; + unsigned i; + for (i = 0; i < count; ++i) { + NSString *type = [newTypes objectAtIndex:i]; + NSString *setType = [typesSet member:type]; + if (!setType) { + setType = [type copy]; + [typesArray addObject:setType]; + [typesSet addObject:setType]; + [setType release]; + } + if (newOwner && [newOwner respondsToSelector:@selector(pasteboard:provideDataForType:)]) + [newOwner pasteboard:self provideDataForType:setType]; + } + return ++changeCount; +} + +- (NSInteger)changeCount +{ + return changeCount; +} + +- (NSArray *)types +{ + return typesArray; +} + +- (NSString *)availableTypeFromArray:(NSArray *)types +{ + unsigned count = [types count]; + unsigned i; + for (i = 0; i < count; ++i) { + NSString *type = [types objectAtIndex:i]; + NSString *setType = [typesSet member:type]; + if (setType) + return setType; + } + return nil; +} + +- (BOOL)setData:(NSData *)data forType:(NSString *)dataType +{ + if (data == nil) + data = [NSData data]; + if (![typesSet containsObject:dataType]) + return NO; + [dataByType setObject:data forKey:dataType]; + ++changeCount; + return YES; +} + +- (NSData *)dataForType:(NSString *)dataType +{ + return [dataByType objectForKey:dataType]; +} + +- (BOOL)setPropertyList:(id)propertyList forType:(NSString *)dataType; +{ + CFDataRef data = NULL; + if (propertyList) + data = CFPropertyListCreateXMLData(NULL, propertyList); + BOOL result = [self setData:(NSData *)data forType:dataType]; + if (data) + CFRelease(data); + return result; +} + +- (BOOL)setString:(NSString *)string forType:(NSString *)dataType +{ + CFDataRef data = NULL; + if (string) { + if ([string length] == 0) + data = CFDataCreate(NULL, NULL, 0); + else + data = CFStringCreateExternalRepresentation(NULL, (CFStringRef)string, kCFStringEncodingUTF8, 0); + } + BOOL result = [self setData:(NSData *)data forType:dataType]; + if (data) + CFRelease(data); + return result; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h new file mode 100644 index 0000000..d2fb60d --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <AppKit/AppKit.h> + +@class WebView; + +@interface DumpRenderTreeWindow : NSWindow +// I'm not sure why we can't just use [NSApp windows] ++ (NSArray *)openWindows; + +- (WebView *)webView; + +- (void)startObservingWebView; +- (void)stopObservingWebView; + +@end diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm new file mode 100644 index 0000000..3b1fc1f --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTreeWindow.h" + +#import "DumpRenderTree.h" + +// FIXME: This file is ObjC++ only because of this include. :( +#import "LayoutTestController.h" +#import <WebKit/WebViewPrivate.h> +#import <WebKit/WebTypesInternal.h> + +CFMutableArrayRef openWindowsRef = 0; + +static CFArrayCallBacks NonRetainingArrayCallbacks = { + 0, + NULL, + NULL, + CFCopyDescription, + CFEqual +}; + +@implementation DumpRenderTreeWindow + ++ (NSArray *)openWindows +{ + return [[(NSArray *)openWindowsRef copy] autorelease]; +} + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation +{ + if (!openWindowsRef) + openWindowsRef = CFArrayCreateMutable(NULL, 0, &NonRetainingArrayCallbacks); + + CFArrayAppendValue(openWindowsRef, self); + + return [super initWithContentRect:contentRect styleMask:styleMask backing:bufferingType defer:deferCreation]; +} + +- (void)close +{ + [self stopObservingWebView]; + + CFRange arrayRange = CFRangeMake(0, CFArrayGetCount(openWindowsRef)); + CFIndex i = CFArrayGetFirstIndexOfValue(openWindowsRef, arrayRange, self); + assert(i != -1); + CFArrayRemoveValueAtIndex(openWindowsRef, i); + + [super close]; +} + +- (BOOL)isKeyWindow +{ + return gLayoutTestController ? gLayoutTestController->windowIsKey() : YES; +} + +- (void)keyDown:(id)sender +{ + // Do nothing, avoiding the beep we'd otherwise get from NSResponder, + // once we get to the end of the responder chain. +} + +- (WebView *)webView +{ + NSView *firstView = nil; + if ([[[self contentView] subviews] count] > 0) { + firstView = [[[self contentView] subviews] objectAtIndex:0]; + if ([firstView isKindOfClass:[WebView class]]) + return static_cast<WebView *>(firstView); + } + return nil; +} + +- (void)startObservingWebView +{ + [[self webView] addObserver:self forKeyPath:@"_isUsingAcceleratedCompositing" options:0 context:0]; +} + +- (void)stopObservingWebView +{ + [[self webView] removeObserver:self forKeyPath:@"_isUsingAcceleratedCompositing"]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:@"_isUsingAcceleratedCompositing"]) { + // When using accelerated compositing, the window needs to be autodisplay for AppKit/CA to + // start accelerated animations correctly. + BOOL isAccelerated = [[self webView] _isUsingAcceleratedCompositing]; + [self setAutodisplay:isAccelerated]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/EditingDelegate.h b/WebKitTools/DumpRenderTree/mac/EditingDelegate.h new file mode 100644 index 0000000..b5563c8 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/EditingDelegate.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +@interface EditingDelegate : NSObject +{ + BOOL acceptsEditing; +} + +- (void)setAcceptsEditing:(BOOL)newAcceptsEditing; + +@end diff --git a/WebKitTools/DumpRenderTree/mac/EditingDelegate.mm b/WebKitTools/DumpRenderTree/mac/EditingDelegate.mm new file mode 100644 index 0000000..cf4026b --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/EditingDelegate.mm @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "EditingDelegate.h" + +#import "DumpRenderTree.h" +#import "LayoutTestController.h" +#import <WebKit/WebKit.h> + +@interface DOMNode (dumpPath) +- (NSString *)dumpPath; +@end + +@implementation DOMNode (dumpPath) +- (NSString *)dumpPath +{ + DOMNode *parent = [self parentNode]; + NSString *str = [NSString stringWithFormat:@"%@", [self nodeName]]; + if (parent != nil) { + str = [str stringByAppendingString:@" > "]; + str = [str stringByAppendingString:[parent dumpPath]]; + } + return str; +} +@end + +@interface DOMRange (dump) +- (NSString *)dump; +@end + +@implementation DOMRange (dump) +- (NSString *)dump +{ + return [NSString stringWithFormat:@"range from %ld of %@ to %ld of %@", [self startOffset], [[self startContainer] dumpPath], [self endOffset], [[self endContainer] dumpPath]]; +} +@end + +@implementation EditingDelegate + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + acceptsEditing = YES; + return self; +} + +- (BOOL)webView:(WebView *)webView shouldBeginEditingInDOMRange:(DOMRange *)range +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n", [[range dump] UTF8String]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldEndEditingInDOMRange:(DOMRange *)range +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n", [[range dump] UTF8String]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldInsertNode:(DOMNode *)node replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action +{ + static const char *insertactionstring[] = { + "WebViewInsertActionTyped", + "WebViewInsertActionPasted", + "WebViewInsertActionDropped", + }; + + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n", [[node dumpPath] UTF8String], [[range dump] UTF8String], insertactionstring[action]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action +{ + static const char *insertactionstring[] = { + "WebViewInsertActionTyped", + "WebViewInsertActionPasted", + "WebViewInsertActionDropped", + }; + + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n", [[text description] UTF8String], [[range dump] UTF8String], insertactionstring[action]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldDeleteDOMRange:(DOMRange *)range +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", [[range dump] UTF8String]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldShowDeleteInterfaceForElement:(DOMHTMLElement *)element +{ + return [[element className] isEqualToString:@"needsDeletionUI"]; +} + +- (BOOL)webView:(WebView *)webView shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag +{ + static const char *affinitystring[] = { + "NSSelectionAffinityUpstream", + "NSSelectionAffinityDownstream" + }; + static const char *boolstring[] = { + "FALSE", + "TRUE" + }; + + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n", [[currentRange dump] UTF8String], [[proposedRange dump] UTF8String], affinitystring[selectionAffinity], boolstring[flag]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldApplyStyle:(DOMCSSStyleDeclaration *)style toElementsInDOMRange:(DOMRange *)range +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n", [[style description] UTF8String], [[range dump] UTF8String]); + return acceptsEditing; +} + +- (BOOL)webView:(WebView *)webView shouldChangeTypingStyle:(DOMCSSStyleDeclaration *)currentStyle toStyle:(DOMCSSStyleDeclaration *)proposedStyle +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: shouldChangeTypingStyle:%s toStyle:%s\n", [[currentStyle description] UTF8String], [[proposedStyle description] UTF8String]); + return acceptsEditing; +} + +- (void)webViewDidBeginEditing:(NSNotification *)notification +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: webViewDidBeginEditing:%s\n", [[notification name] UTF8String]); +} + +- (void)webViewDidChange:(NSNotification *)notification +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: webViewDidChange:%s\n", [[notification name] UTF8String]); +} + +- (void)webViewDidEndEditing:(NSNotification *)notification +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: webViewDidEndEditing:%s\n", [[notification name] UTF8String]); +} + +- (void)webViewDidChangeTypingStyle:(NSNotification *)notification +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: webViewDidChangeTypingStyle:%s\n", [[notification name] UTF8String]); +} + +- (void)webViewDidChangeSelection:(NSNotification *)notification +{ + if (!done && gLayoutTestController->dumpEditingCallbacks()) + printf("EDITING DELEGATE: webViewDidChangeSelection:%s\n", [[notification name] UTF8String]); +} + +- (void)setAcceptsEditing:(BOOL)newAcceptsEditing +{ + acceptsEditing = newAcceptsEditing; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/EventSendingController.h b/WebKitTools/DumpRenderTree/mac/EventSendingController.h new file mode 100644 index 0000000..9440575 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/EventSendingController.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> +#import <WebKit/WebKit.h> + +@interface EventSendingController : NSObject <DOMEventListener> +{ + BOOL leftMouseButtonDown; + BOOL dragMode; + int clickCount; + NSTimeInterval lastClick; + int eventNumber; + double timeOffset; +} + ++ (void)saveEvent:(NSInvocation *)event; ++ (void)replaySavedEvents; ++ (void)clearSavedEvents; + +- (void)scheduleAsynchronousClick; + +- (void)enableDOMUIEventLogging:(WebScriptObject *)node; + +- (void)handleEvent:(DOMEvent *)event; + +@end + +extern NSPoint lastMousePosition; +extern NSPoint lastClickPosition;
\ No newline at end of file diff --git a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm new file mode 100644 index 0000000..5883026 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Jonas Witt <jonas.witt@gmail.com> + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "EventSendingController.h" + +#import "DumpRenderTree.h" +#import "DumpRenderTreeDraggingInfo.h" + +#import <Carbon/Carbon.h> // for GetCurrentEventTime() +#import <WebKit/DOMPrivate.h> +#import <WebKit/WebKit.h> +#import <WebKit/WebViewPrivate.h> + +extern "C" void _NSNewKillRingSequence(); + +enum MouseAction { + MouseDown, + MouseUp, + MouseDragged +}; + +// Match the DOM spec (sadly the DOM spec does not provide an enum) +enum MouseButton { + LeftMouseButton = 0, + MiddleMouseButton = 1, + RightMouseButton = 2, + NoMouseButton = -1 +}; + +NSPoint lastMousePosition; +NSPoint lastClickPosition; +int lastClickButton = NoMouseButton; +NSArray *webkitDomEventNames; +NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once. +BOOL replayingSavedEvents; + +@implementation EventSendingController + ++ (void)initialize +{ + webkitDomEventNames = [[NSArray alloc] initWithObjects: + @"abort", + @"beforecopy", + @"beforecut", + @"beforepaste", + @"blur", + @"change", + @"click", + @"contextmenu", + @"copy", + @"cut", + @"dblclick", + @"drag", + @"dragend", + @"dragenter", + @"dragleave", + @"dragover", + @"dragstart", + @"drop", + @"error", + @"focus", + @"input", + @"keydown", + @"keypress", + @"keyup", + @"load", + @"mousedown", + @"mousemove", + @"mouseout", + @"mouseover", + @"mouseup", + @"mousewheel", + @"beforeunload", + @"paste", + @"readystatechange", + @"reset", + @"resize", + @"scroll", + @"search", + @"select", + @"selectstart", + @"submit", + @"textInput", + @"textzoomin", + @"textzoomout", + @"unload", + @"zoom", + nil]; +} + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + if (aSelector == @selector(mouseDown:) + || aSelector == @selector(mouseUp:) + || aSelector == @selector(contextClick) + || aSelector == @selector(scheduleAsynchronousClick) + || aSelector == @selector(mouseMoveToX:Y:) + || aSelector == @selector(leapForward:) + || aSelector == @selector(keyDown:withModifiers:) + || aSelector == @selector(enableDOMUIEventLogging:) + || aSelector == @selector(fireKeyboardEventsToElement:) + || aSelector == @selector(clearKillRing) + || aSelector == @selector(textZoomIn) + || aSelector == @selector(textZoomOut) + || aSelector == @selector(zoomPageIn) + || aSelector == @selector(zoomPageOut)) + return NO; + return YES; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char*)name +{ + if (strcmp(name, "dragMode") == 0) + return NO; + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(mouseDown:)) + return @"mouseDown"; + if (aSelector == @selector(mouseUp:)) + return @"mouseUp"; + if (aSelector == @selector(mouseMoveToX:Y:)) + return @"mouseMoveTo"; + if (aSelector == @selector(leapForward:)) + return @"leapForward"; + if (aSelector == @selector(keyDown:withModifiers:)) + return @"keyDown"; + if (aSelector == @selector(enableDOMUIEventLogging:)) + return @"enableDOMUIEventLogging"; + if (aSelector == @selector(fireKeyboardEventsToElement:)) + return @"fireKeyboardEventsToElement"; + if (aSelector == @selector(setDragMode:)) + return @"setDragMode"; + return nil; +} + +- (id)init +{ + self = [super init]; + if (self) + dragMode = YES; + return self; +} + +- (void)dealloc +{ + [super dealloc]; +} + +- (double)currentEventTime +{ + return GetCurrentEventTime() + timeOffset; +} + +- (void)leapForward:(int)milliseconds +{ + if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]]; + [invocation setTarget:self]; + [invocation setSelector:@selector(leapForward:)]; + [invocation setArgument:&milliseconds atIndex:2]; + + [EventSendingController saveEvent:invocation]; + + return; + } + + timeOffset += milliseconds / 1000.0; +} + +- (void)clearKillRing +{ + _NSNewKillRingSequence(); +} + +static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action) +{ + switch (button) { + case LeftMouseButton: + switch (action) { + case MouseDown: + return NSLeftMouseDown; + case MouseUp: + return NSLeftMouseUp; + case MouseDragged: + return NSLeftMouseDragged; + } + case RightMouseButton: + switch (action) { + case MouseDown: + return NSRightMouseDown; + case MouseUp: + return NSRightMouseUp; + case MouseDragged: + return NSRightMouseDragged; + } + default: + switch (action) { + case MouseDown: + return NSOtherMouseDown; + case MouseUp: + return NSOtherMouseUp; + case MouseDragged: + return NSOtherMouseDragged; + } + } + assert(0); + return static_cast<NSEventType>(0); +} + +- (void)updateClickCountForButton:(int)buttonNumber +{ + if (([self currentEventTime] - lastClick >= 1) || + !NSEqualPoints(lastMousePosition, lastClickPosition) || + lastClickButton != buttonNumber) { + clickCount = 1; + lastClickButton = buttonNumber; + } else + clickCount++; +} + +- (void)mouseDown:(int)buttonNumber +{ + [[[mainFrame frameView] documentView] layout]; + [self updateClickCountForButton:buttonNumber]; + + NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown); + NSEvent *event = [NSEvent mouseEventWithType:eventType + location:lastMousePosition + modifierFlags:0 + timestamp:[self currentEventTime] + windowNumber:[[[mainFrame webView] window] windowNumber] + context:[NSGraphicsContext currentContext] + eventNumber:++eventNumber + clickCount:clickCount + pressure:0.0]; + + NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; + if (subView) { + [subView mouseDown:event]; + if (buttonNumber == LeftMouseButton) + leftMouseButtonDown = YES; + } +} + +- (void)textZoomIn +{ + [[mainFrame webView] makeTextLarger:self]; +} + +- (void)textZoomOut +{ + [[mainFrame webView] makeTextSmaller:self]; +} + +- (void)zoomPageIn +{ + [[mainFrame webView] zoomPageIn:self]; +} + +- (void)zoomPageOut +{ + [[mainFrame webView] zoomPageOut:self]; +} + +- (void)mouseUp:(int)buttonNumber +{ + if (dragMode && !replayingSavedEvents) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:)]]; + [invocation setTarget:self]; + [invocation setSelector:@selector(mouseUp:)]; + [invocation setArgument:&buttonNumber atIndex:2]; + + [EventSendingController saveEvent:invocation]; + [EventSendingController replaySavedEvents]; + + return; + } + + [[[mainFrame frameView] documentView] layout]; + NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp); + NSEvent *event = [NSEvent mouseEventWithType:eventType + location:lastMousePosition + modifierFlags:0 + timestamp:[self currentEventTime] + windowNumber:[[[mainFrame webView] window] windowNumber] + context:[NSGraphicsContext currentContext] + eventNumber:++eventNumber + clickCount:clickCount + pressure:0.0]; + + NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]]; + // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView. + // The right solution is just to use NSApplication's built-in event sending methods, + // instead of rolling our own algorithm for selecting an event target. + targetView = targetView ? targetView : [[mainFrame frameView] documentView]; + assert(targetView); + [targetView mouseUp:event]; + if (buttonNumber == LeftMouseButton) + leftMouseButtonDown = NO; + lastClick = [event timestamp]; + lastClickPosition = lastMousePosition; + if (draggingInfo) { + WebView *webView = [mainFrame webView]; + + NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo]; + + if (dragOperation != NSDragOperationNone) + [webView performDragOperation:draggingInfo]; + else + [webView draggingExited:draggingInfo]; + [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation]; + [draggingInfo release]; + draggingInfo = nil; + } +} + +- (void)mouseMoveToX:(int)x Y:(int)y +{ + if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]]; + [invocation setTarget:self]; + [invocation setSelector:@selector(mouseMoveToX:Y:)]; + [invocation setArgument:&x atIndex:2]; + [invocation setArgument:&y atIndex:3]; + + [EventSendingController saveEvent:invocation]; + + return; + } + + NSView *view = [mainFrame webView]; + lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil]; + NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved) + location:lastMousePosition + modifierFlags:0 + timestamp:[self currentEventTime] + windowNumber:[[view window] windowNumber] + context:[NSGraphicsContext currentContext] + eventNumber:++eventNumber + clickCount:(leftMouseButtonDown ? clickCount : 0) + pressure:0.0]; + + NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; + if (subView) { + if (leftMouseButtonDown) { + [subView mouseDragged:event]; + if (draggingInfo) { + [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition]; + [[mainFrame webView] draggingUpdated:draggingInfo]; + } + } else + [subView mouseMoved:event]; + } +} + +- (void)contextClick +{ + [[[mainFrame frameView] documentView] layout]; + [self updateClickCountForButton:RightMouseButton]; + + NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown + location:lastMousePosition + modifierFlags:0 + timestamp:[self currentEventTime] + windowNumber:[[[mainFrame webView] window] windowNumber] + context:[NSGraphicsContext currentContext] + eventNumber:++eventNumber + clickCount:clickCount + pressure:0.0]; + + NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; + if (subView) + [subView menuForEvent:event]; +} + +- (void)scheduleAsynchronousClick +{ + [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0]; + [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0]; +} + ++ (void)saveEvent:(NSInvocation *)event +{ + if (!savedMouseEvents) + savedMouseEvents = [[NSMutableArray alloc] init]; + [savedMouseEvents addObject:event]; +} + ++ (void)replaySavedEvents +{ + replayingSavedEvents = YES; + while ([savedMouseEvents count]) { + // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate + NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease]; + [savedMouseEvents removeObjectAtIndex:0]; + [invocation invoke]; + } + replayingSavedEvents = NO; +} + ++ (void)clearSavedEvents +{ + [savedMouseEvents release]; + savedMouseEvents = nil; +} + +- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers +{ + NSString *eventCharacter = character; + if ([character isEqualToString:@"leftArrow"]) { + const unichar ch = NSLeftArrowFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"rightArrow"]) { + const unichar ch = NSRightArrowFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"upArrow"]) { + const unichar ch = NSUpArrowFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"downArrow"]) { + const unichar ch = NSDownArrowFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"pageUp"]) { + const unichar ch = NSPageUpFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"pageDown"]) { + const unichar ch = NSPageDownFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"home"]) { + const unichar ch = NSHomeFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"end"]) { + const unichar ch = NSEndFunctionKey; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } else if ([character isEqualToString:@"delete"]) { + const unichar ch = 0x7f; + eventCharacter = [NSString stringWithCharacters:&ch length:1]; + } + + NSString *charactersIgnoringModifiers = eventCharacter; + + int modifierFlags = 0; + + if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') { + modifierFlags |= NSShiftKeyMask; + 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; + } + + [[[mainFrame frameView] documentView] layout]; + + NSEvent *event = [NSEvent keyEventWithType:NSKeyDown + location:NSMakePoint(5, 5) + modifierFlags:modifierFlags + timestamp:[self currentEventTime] + windowNumber:[[[mainFrame webView] window] windowNumber] + context:[NSGraphicsContext currentContext] + characters:eventCharacter + charactersIgnoringModifiers:charactersIgnoringModifiers + isARepeat:NO + keyCode:0]; + + [[[[mainFrame webView] window] firstResponder] keyDown:event]; + + event = [NSEvent keyEventWithType:NSKeyUp + location:NSMakePoint(5, 5) + modifierFlags:modifierFlags + timestamp:[self currentEventTime] + windowNumber:[[[mainFrame webView] window] windowNumber] + context:[NSGraphicsContext currentContext] + characters:eventCharacter + charactersIgnoringModifiers:charactersIgnoringModifiers + isARepeat:NO + keyCode:0]; + + [[[[mainFrame webView] window] firstResponder] keyUp:event]; +} + +- (void)enableDOMUIEventLogging:(WebScriptObject *)node +{ + NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator]; + id eventName; + while ((eventName = [eventEnumerator nextObject])) { + [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO]; + } +} + +- (void)handleEvent:(DOMEvent *)event +{ + DOMNode *target = [event target]; + + printf("event type: %s\n", [[event type] UTF8String]); + printf(" target: <%s>\n", [[[target nodeName] lowercaseString] UTF8String]); + + if ([event isKindOfClass:[DOMEvent class]]) { + printf(" eventPhase: %d\n", [event eventPhase]); + printf(" bubbles: %d\n", [event bubbles] ? 1 : 0); + printf(" cancelable: %d\n", [event cancelable] ? 1 : 0); + } + + if ([event isKindOfClass:[DOMUIEvent class]]) { + printf(" detail: %d\n", [(DOMUIEvent*)event detail]); + + DOMAbstractView *view = [(DOMUIEvent*)event view]; + if (view) { + printf(" view: OK"); + if ([view document]) + printf(" (document: OK)"); + printf("\n"); + } + } + + if ([event isKindOfClass:[DOMKeyboardEvent class]]) { + printf(" keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]); + printf(" keyLocation: %d\n", [(DOMKeyboardEvent*)event keyLocation]); + printf(" modifier keys: c:%d s:%d a:%d m:%d\n", + [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, + [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, + [(DOMKeyboardEvent*)event altKey] ? 1 : 0, + [(DOMKeyboardEvent*)event metaKey] ? 1 : 0); + printf(" keyCode: %d\n", [(DOMKeyboardEvent*)event keyCode]); + printf(" charCode: %d\n", [(DOMKeyboardEvent*)event charCode]); + } + + if ([event isKindOfClass:[DOMMouseEvent class]]) { + printf(" button: %d\n", [(DOMMouseEvent*)event button]); + printf(" clientX: %d\n", [(DOMMouseEvent*)event clientX]); + printf(" clientY: %d\n", [(DOMMouseEvent*)event clientY]); + printf(" screenX: %d\n", [(DOMMouseEvent*)event screenX]); + printf(" screenY: %d\n", [(DOMMouseEvent*)event screenY]); + printf(" modifier keys: c:%d s:%d a:%d m:%d\n", + [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, + [(DOMMouseEvent*)event shiftKey] ? 1 : 0, + [(DOMMouseEvent*)event altKey] ? 1 : 0, + [(DOMMouseEvent*)event metaKey] ? 1 : 0); + id relatedTarget = [(DOMMouseEvent*)event relatedTarget]; + if (relatedTarget) { + printf(" relatedTarget: %s", [[[relatedTarget class] description] UTF8String]); + if ([relatedTarget isKindOfClass:[DOMNode class]]) + printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]); + printf("\n"); + } + } + + if ([event isKindOfClass:[DOMMutationEvent class]]) { + printf(" prevValue: %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]); + printf(" newValue: %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]); + printf(" attrName: %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]); + printf(" attrChange: %d\n", [(DOMMutationEvent*)event attrChange]); + DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode]; + if (relatedNode) { + printf(" relatedNode: %s (nodeName: %s)\n", + [[[relatedNode class] description] UTF8String], + [[relatedNode nodeName] UTF8String]); + } + } + + if ([event isKindOfClass:[DOMWheelEvent class]]) { + printf(" clientX: %d\n", [(DOMWheelEvent*)event clientX]); + printf(" clientY: %d\n", [(DOMWheelEvent*)event clientY]); + printf(" screenX: %d\n", [(DOMWheelEvent*)event screenX]); + printf(" screenY: %d\n", [(DOMWheelEvent*)event screenY]); + printf(" modifier keys: c:%d s:%d a:%d m:%d\n", + [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, + [(DOMWheelEvent*)event shiftKey] ? 1 : 0, + [(DOMWheelEvent*)event altKey] ? 1 : 0, + [(DOMWheelEvent*)event metaKey] ? 1 : 0); + printf(" isHorizontal: %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0); + printf(" wheelDelta: %d\n", [(DOMWheelEvent*)event wheelDelta]); + } +} + +// FIXME: It's not good to have a test hard-wired into this controller like this. +// Instead we need to get testing framework based on the Objective-C bindings +// to work well enough that we can test that way instead. +- (void)fireKeyboardEventsToElement:(WebScriptObject *)element { + + if (![element isKindOfClass:[DOMHTMLElement class]]) { + return; + } + + DOMHTMLElement *target = (DOMHTMLElement*)element; + DOMDocument *document = [target ownerDocument]; + + // Keyboard Event 1 + + DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"]; + [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" + canBubble:YES + cancelable:YES + view:[document defaultView] + keyIdentifier:@"U+000041" + keyLocation:0 + ctrlKey:YES + altKey:NO + shiftKey:NO + metaKey:NO]; + [target dispatchEvent:domEvent]; + + // Keyboard Event 2 + + domEvent = [document createEvent:@"KeyboardEvent"]; + [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" + canBubble:YES + cancelable:YES + view:[document defaultView] + keyIdentifier:@"U+000045" + keyLocation:1 + ctrlKey:NO + altKey:YES + shiftKey:NO + metaKey:NO]; + [target dispatchEvent:domEvent]; + + // Keyboard Event 3 + + domEvent = [document createEvent:@"KeyboardEvent"]; + [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" + canBubble:YES + cancelable:YES + view:[document defaultView] + keyIdentifier:@"U+000056" + keyLocation:0 + ctrlKey:NO + altKey:NO + shiftKey:NO + metaKey:NO]; + [target dispatchEvent:domEvent]; + +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.h b/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.h new file mode 100644 index 0000000..6c3cbdb --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +class AccessibilityController; +class GCController; + +@interface FrameLoadDelegate : NSObject +{ + AccessibilityController* accessibilityController; + GCController* gcController; +} +@end diff --git a/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm b/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm new file mode 100644 index 0000000..f079cb5 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" +#import "FrameLoadDelegate.h" + +#import "AccessibilityController.h" +#import "AppleScriptController.h" +#import "EventSendingController.h" +#import "GCController.h" +#import "LayoutTestController.h" +#import "NavigationController.h" +#import "ObjCController.h" +#import "ObjCPlugin.h" +#import "ObjCPluginFunction.h" +#import "TextInputController.h" +#import "WorkQueue.h" +#import "WorkQueueItem.h" +#import <JavaScriptCore/JavaScriptCore.h> +#import <WebKit/WebFramePrivate.h> +#import <WebKit/WebHTMLViewPrivate.h> +#import <WebKit/WebKit.h> +#import <WebKit/WebNSURLExtras.h> +#import <wtf/Assertions.h> + +@interface NSURLRequest (PrivateThingsWeShouldntReallyUse) ++(void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host; +@end + +@interface NSURL (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface NSError (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface NSURLResponse (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface NSURLRequest (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface WebFrame (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@implementation WebFrame (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult +{ + BOOL isMainFrame = (self == [[self webView] mainFrame]); + NSString *name = [self name]; + if (isMainFrame) { + if ([name length]) + return [NSString stringWithFormat:@"main frame \"%@\"", name]; + else + return @"main frame"; + } else { + if (name) + return [NSString stringWithFormat:@"frame \"%@\"", name]; + else + return @"frame (anonymous)"; + } +} +@end + +@implementation FrameLoadDelegate + +- (id)init +{ + if ((self = [super init])) { + gcController = new GCController; + accessibilityController = new AccessibilityController; + } + return self; +} + +- (void)dealloc +{ + delete gcController; + [super dealloc]; +} + +// Exec messages in the work queue until they're all done, or one of them starts a new load +- (void)processWork:(id)dummy +{ + // quit doing work once a load is in progress + while (WorkQueue::shared()->count() > 0 && !topLoadingFrame) { + WorkQueueItem* item = WorkQueue::shared()->dequeue(); + ASSERT(item); + item->invoke(); + delete item; + } + + // if we didn't start a new load, then we finished all the commands, so we're ready to dump state + if (!topLoadingFrame && !gLayoutTestController->waitToDump()) + dump(); +} + +- (void)webView:(WebView *)c locationChangeDone:(NSError *)error forDataSource:(WebDataSource *)dataSource +{ + if ([dataSource webFrame] == topLoadingFrame) { + topLoadingFrame = nil; + WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test + if (!gLayoutTestController->waitToDump()) { + if (WorkQueue::shared()->count()) + [self performSelector:@selector(processWork:) withObject:nil afterDelay:0]; + else + dump(); + } + } +} + +- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didStartProvisionalLoadForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } + + ASSERT([frame provisionalDataSource]); + // Make sure we only set this once per test. If it gets cleared, and then set again, we might + // end up doing two dumps for one test. + if (!topLoadingFrame && !done) + topLoadingFrame = frame; + + if (!done && gLayoutTestController->stopProvisionalFrameLoads()) { + NSString *string = [NSString stringWithFormat:@"%@ - stopping load in didStartProvisionalLoadForFrame callback", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + [frame stopLoading]; + } +} + +- (void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didCommitLoadForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } + + ASSERT(![frame provisionalDataSource]); + ASSERT([frame dataSource]); + + gLayoutTestController->setWindowIsKey(true); + NSView *documentView = [[mainFrame frameView] documentView]; + [[[mainFrame webView] window] makeFirstResponder:documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _updateFocusedAndActiveState]; +} + +- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didFailProvisionalLoadWithError", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } + + if ([error domain] == NSURLErrorDomain && [error code] == NSURLErrorServerCertificateHasUnknownRoot) { + NSURL *failedURL = [[error userInfo] objectForKey:@"NSErrorFailingURLKey"]; + [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[failedURL _web_hostString]]; + [frame loadRequest:[[[[frame provisionalDataSource] request] mutableCopy] autorelease]]; + return; + } + + ASSERT([frame provisionalDataSource]); + [self webView:sender locationChangeDone:error forDataSource:[frame provisionalDataSource]]; +} + +- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame +{ + ASSERT([frame dataSource]); + ASSERT(frame == [[frame dataSource] webFrame]); + + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didFinishLoadForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } + + // FIXME: This call to displayIfNeeded can be removed when <rdar://problem/5092361> is fixed. + // After that is fixed, we will reenable painting after WebCore is done loading the document, + // and this call will no longer be needed. + if ([[sender mainFrame] isEqual:frame]) + [sender displayIfNeeded]; + [self webView:sender locationChangeDone:nil forDataSource:[frame dataSource]]; + [gNavigationController webView:sender didFinishLoadForFrame:frame]; +} + +- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didFailLoadWithError", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } + + ASSERT(![frame provisionalDataSource]); + ASSERT([frame dataSource]); + + [self webView:sender locationChangeDone:error forDataSource:[frame dataSource]]; +} + +- (void)webView:(WebView *)webView windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"?? - windowScriptObjectAvailable"]; + printf ("%s\n", [string UTF8String]); + } + + ASSERT_NOT_REACHED(); +} + +- (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)obj forFrame:(WebFrame *)frame +{ + ASSERT(obj == [frame windowObject]); + ASSERT([obj JSObject] == JSContextGetGlobalObject([frame globalContext])); + + // Make New-Style LayoutTestController + JSContextRef context = [frame globalContext]; + JSObjectRef globalObject = JSContextGetGlobalObject(context); + JSValueRef exception = 0; + + ASSERT(gLayoutTestController); + gLayoutTestController->makeWindowObject(context, globalObject, &exception); + ASSERT(!exception); + + gcController->makeWindowObject(context, globalObject, &exception); + ASSERT(!exception); + + accessibilityController->makeWindowObject(context, globalObject, &exception); + ASSERT(!exception); + + // Make Old-Style controllers + EventSendingController *esc = [[EventSendingController alloc] init]; + [obj setValue:esc forKey:@"eventSender"]; + [esc release]; + + TextInputController *tic = [[TextInputController alloc] initWithWebView:sender]; + [obj setValue:tic forKey:@"textInputController"]; + [tic release]; + + AppleScriptController *asc = [[AppleScriptController alloc] initWithWebView:sender]; + [obj setValue:asc forKey:@"appleScriptController"]; + [asc release]; + + ObjCController *occ = [[ObjCController alloc] init]; + [obj setValue:occ forKey:@"objCController"]; + [occ release]; + + [obj setValue:gNavigationController forKey:@"navigationController"]; + + ObjCPlugin *plugin = [[ObjCPlugin alloc] init]; + [obj setValue:plugin forKey:@"objCPlugin"]; + [plugin release]; + + ObjCPluginFunction *pluginFunction = [[ObjCPluginFunction alloc] init]; + [obj setValue:pluginFunction forKey:@"objCPluginFunction"]; + [pluginFunction release]; +} + +- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didReceiveTitle: %@", [frame _drt_descriptionSuitableForTestResult], title]; + printf ("%s\n", [string UTF8String]); + } + + if (gLayoutTestController->dumpTitleChanges()) + printf("TITLE CHANGED: %s\n", [title UTF8String]); +} + +- (void)webView:(WebView *)sender didReceiveServerRedirectForProvisionalLoadForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didReceiveServerRedirectForProvisionalLoadForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } +} + +- (void)webView:(WebView *)sender didChangeLocationWithinPageForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didChangeLocationWithinPageForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } +} + +- (void)webView:(WebView *)sender willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)seconds fireDate:(NSDate *)date forFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - willPerformClientRedirectToURL: %@ ", [frame _drt_descriptionSuitableForTestResult], [URL _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } +} + +- (void)webView:(WebView *)sender didCancelClientRedirectForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didCancelClientRedirectForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } +} + +- (void)webView:(WebView *)sender didFinishDocumentLoadForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didFinishDocumentLoadForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } else if (!done) { + unsigned pendingFrameUnloadEvents = [frame _pendingFrameUnloadEventCount]; + if (pendingFrameUnloadEvents) { + NSString *string = [NSString stringWithFormat:@"%@ - has %u onunload handler(s)", [frame _drt_descriptionSuitableForTestResult], pendingFrameUnloadEvents]; + printf ("%s\n", [string UTF8String]); + } + } +} + +- (void)webView:(WebView *)sender didHandleOnloadEventsForFrame:(WebFrame *)frame +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didHandleOnloadEventsForFrame", [frame _drt_descriptionSuitableForTestResult]]; + printf ("%s\n", [string UTF8String]); + } +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/GCControllerMac.mm b/WebKitTools/DumpRenderTree/mac/GCControllerMac.mm new file mode 100644 index 0000000..4b845ba --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/GCControllerMac.mm @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCController.h" +#import <WebKit/WebCoreStatistics.h> + + +void GCController::collect() const +{ + [WebCoreStatistics garbageCollectJavaScriptObjects]; +} + +void GCController::collectOnAlternateThread(bool waitUntilDone) const +{ + [WebCoreStatistics garbageCollectJavaScriptObjectsOnAlternateThreadForDebugging:waitUntilDone]; +} + +size_t GCController::getJSObjectCount() const +{ + return [WebCoreStatistics javaScriptObjectsCount]; +} diff --git a/WebKitTools/DumpRenderTree/mac/InternalHeaders/WebKit/WebHTMLRepresentationInternal.h b/WebKitTools/DumpRenderTree/mac/InternalHeaders/WebKit/WebHTMLRepresentationInternal.h new file mode 100644 index 0000000..b3d421d --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/InternalHeaders/WebKit/WebHTMLRepresentationInternal.h @@ -0,0 +1 @@ +#include "../../../../WebKit/mac/WebView/WebHTMLRepresentationInternal.h" diff --git a/WebKitTools/DumpRenderTree/mac/InternalHeaders/WebKit/WebTypesInternal.h b/WebKitTools/DumpRenderTree/mac/InternalHeaders/WebKit/WebTypesInternal.h new file mode 100644 index 0000000..58049c2 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/InternalHeaders/WebKit/WebTypesInternal.h @@ -0,0 +1 @@ +#include "../../../../WebKit/mac/Misc/WebTypesInternal.h" diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm new file mode 100644 index 0000000..c80c78f --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" +#import "LayoutTestController.h" + +#import "EditingDelegate.h" +#import "PolicyDelegate.h" +#import "WorkQueue.h" +#import "WorkQueueItem.h" +#import <Foundation/Foundation.h> +#import <JavaScriptCore/JSRetainPtr.h> +#import <JavaScriptCore/JSStringRef.h> +#import <JavaScriptCore/JSStringRefCF.h> +#import <WebKit/DOMDocument.h> +#import <WebKit/DOMElement.h> +#import <WebKit/WebBackForwardList.h> +#import <WebKit/WebDatabaseManagerPrivate.h> +#import <WebKit/WebDataSource.h> +#import <WebKit/WebFrame.h> +#import <WebKit/WebFrameViewPrivate.h> +#import <WebKit/WebIconDatabasePrivate.h> +#import <WebKit/WebHTMLRepresentation.h> +#import <WebKit/WebHTMLViewPrivate.h> +#import <WebKit/WebHistory.h> +#import <WebKit/WebHistoryPrivate.h> +#import <WebKit/WebInspector.h> +#import <WebKit/WebNSURLExtras.h> +#import <WebKit/WebPreferences.h> +#import <WebKit/WebPreferencesPrivate.h> +#import <WebKit/WebSecurityOriginPrivate.h> +#import <WebKit/WebTypesInternal.h> +#import <WebKit/WebView.h> +#import <WebKit/WebViewPrivate.h> +#import <wtf/RetainPtr.h> + +@interface CommandValidationTarget : NSObject <NSValidatedUserInterfaceItem> +{ + SEL _action; +} +- (id)initWithAction:(SEL)action; +@end + +@implementation CommandValidationTarget + +- (id)initWithAction:(SEL)action +{ + self = [super init]; + if (!self) + return nil; + + _action = action; + return self; +} + +- (SEL)action +{ + return _action; +} + +- (NSInteger)tag +{ + return 0; +} + +@end + +LayoutTestController::~LayoutTestController() +{ +} + +void LayoutTestController::addDisallowedURL(JSStringRef url) +{ + RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url)); + + if (!disallowedURLs) + disallowedURLs = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); + + // Canonicalize the URL + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]]; + request = [NSURLProtocol canonicalRequestForRequest:request]; + + CFSetAddValue(disallowedURLs, [request URL]); +} + +void LayoutTestController::clearAllDatabases() +{ + [[WebDatabaseManager sharedWebDatabaseManager] deleteAllDatabases]; +} + +void LayoutTestController::clearBackForwardList() +{ + WebBackForwardList *backForwardList = [[mainFrame webView] backForwardList]; + WebHistoryItem *item = [[backForwardList currentItem] retain]; + + // We clear the history by setting the back/forward list's capacity to 0 + // then restoring it back and adding back the current item. + int capacity = [backForwardList capacity]; + [backForwardList setCapacity:0]; + [backForwardList setCapacity:capacity]; + [backForwardList addItem:item]; + [backForwardList goToItem:item]; + [item release]; +} + +JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name) +{ + RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name)); + NSString *nameNS = (NSString *)nameCF.get(); + return JSStringCreateWithCFString((CFStringRef)[nameNS _web_decodeHostName]); +} + +JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name) +{ + RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name)); + NSString *nameNS = (NSString *)nameCF.get(); + return JSStringCreateWithCFString((CFStringRef)[nameNS _web_encodeHostName]); +} + +void LayoutTestController::display() +{ + displayWebView(); +} + +void LayoutTestController::keepWebHistory() +{ + if (![WebHistory optionalSharedHistory]) { + WebHistory *history = [[WebHistory alloc] init]; + [WebHistory setOptionalSharedHistory:history]; + [history release]; + } +} + +size_t LayoutTestController::webHistoryItemCount() +{ + return [[[WebHistory optionalSharedHistory] allItems] count]; +} + +void LayoutTestController::notifyDone() +{ + if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count()) + dump(); + m_waitToDump = false; +} + +JSStringRef LayoutTestController::pathToLocalResource(JSContextRef context, JSStringRef url) +{ + return JSStringRetain(url); // Do nothing on mac. +} + +void LayoutTestController::queueBackNavigation(int howFarBack) +{ + WorkQueue::shared()->queue(new BackItem(howFarBack)); +} + +void LayoutTestController::queueForwardNavigation(int howFarForward) +{ + WorkQueue::shared()->queue(new ForwardItem(howFarForward)); +} + +void LayoutTestController::queueLoad(JSStringRef url, JSStringRef target) +{ + RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url)); + NSString *urlNS = (NSString *)urlCF.get(); + + NSURL *nsurl = [NSURL URLWithString:urlNS relativeToURL:[[[mainFrame dataSource] response] URL]]; + NSString* nsurlString = [nsurl absoluteString]; + + JSRetainPtr<JSStringRef> absoluteURL(Adopt, JSStringCreateWithUTF8CString([nsurlString UTF8String])); + WorkQueue::shared()->queue(new LoadItem(absoluteURL.get(), target)); +} + +void LayoutTestController::queueReload() +{ + WorkQueue::shared()->queue(new ReloadItem); +} + +void LayoutTestController::queueScript(JSStringRef script) +{ + WorkQueue::shared()->queue(new ScriptItem(script)); +} + +void LayoutTestController::setAcceptsEditing(bool newAcceptsEditing) +{ + [(EditingDelegate *)[[mainFrame webView] editingDelegate] setAcceptsEditing:newAcceptsEditing]; +} + +void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag) +{ + [[[mainFrame webView] preferences] setAuthorAndUserStylesEnabled:flag]; +} + +void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive) +{ + if (setDelegate) { + [policyDelegate setPermissive:permissive]; + [[mainFrame webView] setPolicyDelegate:policyDelegate]; + } else + [[mainFrame webView] setPolicyDelegate:nil]; +} + +void LayoutTestController::setDatabaseQuota(unsigned long long quota) +{ + WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:@"file:///"]]; + [origin setQuota:quota]; + [origin release]; +} + +void LayoutTestController::setIconDatabaseEnabled(bool iconDatabaseEnabled) +{ + // FIXME: Workaround <rdar://problem/6480108> + static WebIconDatabase* sharedWebIconDatabase = NULL; + if (!sharedWebIconDatabase) { + if (!iconDatabaseEnabled) + return; + sharedWebIconDatabase = [WebIconDatabase sharedIconDatabase]; + if ([sharedWebIconDatabase isEnabled] == iconDatabaseEnabled) + return; + } + [sharedWebIconDatabase setEnabled:iconDatabaseEnabled]; +} + +void LayoutTestController::setJavaScriptProfilingEnabled(bool profilingEnabled) +{ + [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:profilingEnabled]; + [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:profilingEnabled]; +} + +void LayoutTestController::setMainFrameIsFirstResponder(bool flag) +{ + NSView *documentView = [[mainFrame frameView] documentView]; + + NSResponder *firstResponder = flag ? documentView : nil; + [[[mainFrame webView] window] makeFirstResponder:firstResponder]; + + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _updateFocusedAndActiveState]; +} + +void LayoutTestController::setPrivateBrowsingEnabled(bool privateBrowsingEnabled) +{ + [[[mainFrame webView] preferences] setPrivateBrowsingEnabled:privateBrowsingEnabled]; +} + +void LayoutTestController::setPopupBlockingEnabled(bool popupBlockingEnabled) +{ + [[[mainFrame webView] preferences] setJavaScriptCanOpenWindowsAutomatically:!popupBlockingEnabled]; +} + +void LayoutTestController::setTabKeyCyclesThroughElements(bool cycles) +{ + [[mainFrame webView] setTabKeyCyclesThroughElements:cycles]; +} + +void LayoutTestController::setUseDashboardCompatibilityMode(bool flag) +{ + [[mainFrame webView] _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:flag]; +} + +void LayoutTestController::setUserStyleSheetEnabled(bool flag) +{ + [[WebPreferences standardPreferences] setUserStyleSheetEnabled:flag]; +} + +void LayoutTestController::setUserStyleSheetLocation(JSStringRef path) +{ + RetainPtr<CFStringRef> pathCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, path)); + NSURL *url = [NSURL URLWithString:(NSString *)pathCF.get()]; + [[WebPreferences standardPreferences] setUserStyleSheetLocation:url]; +} + +void LayoutTestController::setPersistentUserStyleSheetLocation(JSStringRef jsURL) +{ + RetainPtr<CFStringRef> urlString(AdoptCF, JSStringCopyCFString(0, jsURL)); + ::setPersistentUserStyleSheetLocation(urlString.get()); +} + +void LayoutTestController::clearPersistentUserStyleSheet() +{ + ::setPersistentUserStyleSheetLocation(0); +} + +void LayoutTestController::setWindowIsKey(bool windowIsKey) +{ + m_windowIsKey = windowIsKey; + NSView *documentView = [[mainFrame frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _updateFocusedAndActiveState]; +} + +void LayoutTestController::setSmartInsertDeleteEnabled(bool flag) +{ + [[mainFrame webView] setSmartInsertDeleteEnabled:flag]; +} + +void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag) +{ + [[mainFrame webView] setSelectTrailingWhitespaceEnabled:flag]; +} + +static const CFTimeInterval waitToDumpWatchdogInterval = 10.0; + +static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info) +{ + const char* message = "FAIL: Timed out waiting for notifyDone to be called\n"; + fprintf(stderr, "%s", message); + fprintf(stdout, "%s", message); + dump(); +} + +void LayoutTestController::setWaitToDump(bool waitUntilDone) +{ + m_waitToDump = waitUntilDone; + if (m_waitToDump && !waitToDumpWatchdog) { + waitToDumpWatchdog = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + waitToDumpWatchdogInterval, 0, 0, 0, waitUntilDoneWatchdogFired, NULL); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), waitToDumpWatchdog, kCFRunLoopCommonModes); + } +} + +int LayoutTestController::windowCount() +{ + return CFArrayGetCount(openWindowsRef); +} + +bool LayoutTestController::elementDoesAutoCompleteForElementWithId(JSStringRef id) +{ + RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id)); + NSString *idNS = (NSString *)idCF.get(); + + DOMElement *element = [[mainFrame DOMDocument] getElementById:idNS]; + id rep = [[mainFrame dataSource] representation]; + + if ([rep class] == [WebHTMLRepresentation class]) + return [(WebHTMLRepresentation *)rep elementDoesAutoComplete:element]; + + return false; +} + +void LayoutTestController::execCommand(JSStringRef name, JSStringRef value) +{ + RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name)); + NSString *nameNS = (NSString *)nameCF.get(); + + RetainPtr<CFStringRef> valueCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, value)); + NSString *valueNS = (NSString *)valueCF.get(); + + [[mainFrame webView] _executeCoreCommandByName:nameNS value:valueNS]; +} + +bool LayoutTestController::isCommandEnabled(JSStringRef name) +{ + RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name)); + NSString *nameNS = reinterpret_cast<const NSString *>(nameCF.get()); + + // Accept command strings with capital letters for first letter without trailing colon. + if (![nameNS hasSuffix:@":"] && [nameNS length]) { + nameNS = [[[[nameNS substringToIndex:1] lowercaseString] + stringByAppendingString:[nameNS substringFromIndex:1]] + stringByAppendingString:@":"]; + } + + SEL selector = NSSelectorFromString(nameNS); + RetainPtr<CommandValidationTarget> target(AdoptNS, [[CommandValidationTarget alloc] initWithAction:selector]); + id validator = [NSApp targetForAction:selector to:[mainFrame webView] from:target.get()]; + if (!validator) + return false; + if (![validator respondsToSelector:selector]) + return false; + if (![validator respondsToSelector:@selector(validateUserInterfaceItem:)]) + return true; + return [validator validateUserInterfaceItem:target.get()]; +} + +bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId) +{ + RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId)); + NSString *idNS = (NSString *)idCF.get(); + RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, animationName)); + NSString *nameNS = (NSString *)nameCF.get(); + + return [mainFrame _pauseAnimation:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time]; +} + +bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId) +{ + RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId)); + NSString *idNS = (NSString *)idCF.get(); + RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, propertyName)); + NSString *nameNS = (NSString *)nameCF.get(); + + return [mainFrame _pauseTransitionOfProperty:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time]; +} + +unsigned LayoutTestController::numberOfActiveAnimations() const +{ + return [mainFrame _numberOfActiveAnimations]; +} diff --git a/WebKitTools/DumpRenderTree/mac/NavigationController.h b/WebKitTools/DumpRenderTree/mac/NavigationController.h new file mode 100644 index 0000000..8ee3432 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/NavigationController.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> +#import <WebKit/WebView.h> + +@interface NavigationController : NSObject +{ + enum { None, Load, GoBack, ExecuteScript } pendingAction; + NSString *pendingScript; + NSURLRequest *pendingRequest; +} +- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame; +@end diff --git a/WebKitTools/DumpRenderTree/mac/NavigationController.m b/WebKitTools/DumpRenderTree/mac/NavigationController.m new file mode 100644 index 0000000..44aed8a --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/NavigationController.m @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <WebKit/WebFrame.h> +#import <WebKit/WebScriptObject.h> +#import "NavigationController.h" + +@implementation NavigationController ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector +{ + if (selector == @selector(evaluateWebScript:afterBackForwardNavigation:)) + return NO; + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)selector +{ + if (selector == @selector(evaluateWebScript:afterBackForwardNavigation:)) + return @"evalAfterBackForwardNavigation"; + return nil; +} + +- (void)setPendingScript:(NSString *)script +{ + if (script != pendingScript) { + [pendingScript release]; + pendingScript = [script copy]; + } +} + +- (void)setPendingRequest:(NSURLRequest *)request +{ + if (request != pendingRequest) { + [pendingRequest release]; + pendingRequest = [request copy]; + } +} + +- (void)evaluateWebScript:(NSString *)script afterBackForwardNavigation:(NSString *)navigation +{ + // Allow both arguments to be optional + if (![script isKindOfClass:[NSString class]]) + script = @""; + if (![navigation isKindOfClass:[NSString class]]) + navigation = @"about:blank"; + + [self setPendingScript:script]; + [self setPendingRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:navigation]]]; + pendingAction = Load; +} + +- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame +{ + if (frame == [[frame webView] mainFrame]) { + switch (pendingAction) { + case Load: + pendingAction = GoBack; + [frame loadRequest:pendingRequest]; + [self setPendingRequest:nil]; + break; + case GoBack: + pendingAction = ExecuteScript; + [[frame webView] goBack]; + break; + case ExecuteScript: + pendingAction = None; + [[[frame webView] windowScriptObject] evaluateWebScript:pendingScript]; + [self setPendingScript:nil]; + break; + case None: + default: + break; + } + } +} + +- (void)dealloc +{ + [self setPendingScript:nil]; + [self setPendingRequest:nil]; + [super dealloc]; +} +@end + diff --git a/WebKitTools/DumpRenderTree/mac/ObjCController.h b/WebKitTools/DumpRenderTree/mac/ObjCController.h new file mode 100644 index 0000000..d1d001c --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ObjCController.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +@class WebScriptObject; + +// This controller should be used to test Objective-C language features and the WebScriptObject. +@interface ObjCController : NSObject +{ + WebScriptObject *storedWebScriptObject; +} +@end diff --git a/WebKitTools/DumpRenderTree/mac/ObjCController.m b/WebKitTools/DumpRenderTree/mac/ObjCController.m new file mode 100644 index 0000000..ec1ed38 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ObjCController.m @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "ObjCController.h" + +#import <JavaScriptCore/JavaScriptCore.h> +#import <WebKit/DOMAbstractView.h> +#import <WebKit/WebScriptObject.h> +#import <WebKit/WebView.h> +#import <pthread.h> +#import <wtf/Assertions.h> + +static void* runJavaScriptThread(void* arg) +{ + JSGlobalContextRef ctx = JSGlobalContextCreate(0); + JSStringRef scriptRef = JSStringCreateWithUTF8CString("'Hello World!'"); + + JSValueRef exception = 0; + JSEvaluateScript(ctx, scriptRef, 0, 0, 1, &exception); + ASSERT(!exception); + + JSGlobalContextRelease(ctx); + JSStringRelease(scriptRef); + + return 0; +} + +@implementation ObjCController + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + if (0 + || aSelector == @selector(classNameOf:) + || aSelector == @selector(objectOfClass:) + || aSelector == @selector(identityIsEqual::) + || aSelector == @selector(longLongRoundTrip:) + || aSelector == @selector(unsignedLongLongRoundTrip:) + || aSelector == @selector(testWrapperRoundTripping:) + || aSelector == @selector(accessStoredWebScriptObject) + || aSelector == @selector(storeWebScriptObject:) + || aSelector == @selector(testValueForKey) + || aSelector == @selector(testArray) + ) + return NO; + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(classNameOf:)) + return @"className"; + if (aSelector == @selector(objectOfClass:)) + return @"objectOfClass"; + if (aSelector == @selector(identityIsEqual::)) + return @"identityIsEqual"; + if (aSelector == @selector(longLongRoundTrip:)) + return @"longLongRoundTrip"; + if (aSelector == @selector(unsignedLongLongRoundTrip:)) + return @"unsignedLongLongRoundTrip"; + if (aSelector == @selector(testWrapperRoundTripping:)) + return @"testWrapperRoundTripping"; + if (aSelector == @selector(storeWebScriptObject:)) + return @"storeWebScriptObject"; + if (aSelector == @selector(testValueForKey)) + return @"testValueForKey"; + if (aSelector == @selector(testArray)) + return @"testArray"; + + return nil; +} + +- (NSString *)classNameOf:(id)object +{ + if (!object) + return @"nil"; + return NSStringFromClass([object class]); +} + +- (id)objectOfClass:(NSString *)aClass +{ + if ([aClass isEqualToString:@"NSNull"]) + return [NSNull null]; + if ([aClass isEqualToString:@"WebUndefined"]) + return [WebUndefined undefined]; + if ([aClass isEqualToString:@"NSCFBoolean"]) + return [NSNumber numberWithBool:true]; + if ([aClass isEqualToString:@"NSCFNumber"]) + return [NSNumber numberWithInt:1]; + if ([aClass isEqualToString:@"NSCFString"]) + return @""; + if ([aClass isEqualToString:@"WebScriptObject"]) + return self; + if ([aClass isEqualToString:@"NSArray"]) + return [NSArray array]; + + return nil; +} + +- (BOOL)identityIsEqual:(WebScriptObject *)a :(WebScriptObject *)b +{ + if ([a isKindOfClass:[NSString class]] && [b isKindOfClass:[NSString class]]) + return [(NSString *)a isEqualToString:(NSString *)b]; + return a == b; +} + +- (long long)longLongRoundTrip:(long long)num +{ + return num; +} + +- (unsigned long long)unsignedLongLongRoundTrip:(unsigned long long)num +{ + return num; +} + +- (void)testValueForKey +{ + ASSERT(storedWebScriptObject); + + @try { + [storedWebScriptObject valueForKey:@"ThisKeyDoesNotExist"]; + } @catch (NSException *e) { + } + + pthread_t pthread; + pthread_create(&pthread, 0, &runJavaScriptThread, 0); + pthread_join(pthread, 0); +} + +- (BOOL)testWrapperRoundTripping:(WebScriptObject *)webScriptObject +{ + JSObjectRef jsObject = [webScriptObject JSObject]; + + if (!jsObject) + return false; + + if (!webScriptObject) + return false; + + if ([[webScriptObject evaluateWebScript:@"({ })"] class] != [webScriptObject class]) + return false; + + [webScriptObject setValue:[NSNumber numberWithInt:666] forKey:@"key"]; + if (![[webScriptObject valueForKey:@"key"] isKindOfClass:[NSNumber class]] || + ![[webScriptObject valueForKey:@"key"] isEqualToNumber:[NSNumber numberWithInt:666]]) + return false; + + [webScriptObject removeWebScriptKey:@"key"]; + @try { + if ([webScriptObject valueForKey:@"key"]) + return false; + } @catch(NSException *exception) { + // NSObject throws an exception if the key doesn't exist. + } + + [webScriptObject setWebScriptValueAtIndex:0 value:webScriptObject]; + if ([webScriptObject webScriptValueAtIndex:0] != webScriptObject) + return false; + + if ([[webScriptObject stringRepresentation] isEqualToString:@"[Object object]"]) + return false; + + if ([webScriptObject callWebScriptMethod:@"returnThis" withArguments:nil] != webScriptObject) + return false; + + return true; +} + +- (void)accessStoredWebScriptObject +{ +#if !ASSERT_DISABLED + BOOL isWindowObject = [storedWebScriptObject isKindOfClass:[DOMAbstractView class]]; +#endif + JSObjectRef jsObject = [storedWebScriptObject JSObject]; + ASSERT((jsObject && isWindowObject) || (!jsObject && !isWindowObject)); + + [storedWebScriptObject callWebScriptMethod:@"" withArguments:nil]; + [storedWebScriptObject evaluateWebScript:@""]; + [storedWebScriptObject setValue:[WebUndefined undefined] forKey:@"key"]; + [storedWebScriptObject valueForKey:@"key"]; + [storedWebScriptObject removeWebScriptKey:@"key"]; + [storedWebScriptObject stringRepresentation]; + [storedWebScriptObject webScriptValueAtIndex:0]; + [storedWebScriptObject setWebScriptValueAtIndex:0 value:[WebUndefined undefined]]; + [storedWebScriptObject setException:@"exception"]; +} + +- (void)storeWebScriptObject:(WebScriptObject *)webScriptObject +{ + if (webScriptObject == storedWebScriptObject) + return; + + [storedWebScriptObject release]; + storedWebScriptObject = [webScriptObject retain]; +} + +- (NSArray *)testArray +{ + return [NSArray array]; +} + +- (void)dealloc +{ + [storedWebScriptObject release]; + [super dealloc]; +} + +- (id)invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)args +{ + // FIXME: Perhaps we should log that this has been called. + return nil; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/ObjCPlugin.h b/WebKitTools/DumpRenderTree/mac/ObjCPlugin.h new file mode 100644 index 0000000..a6d3e50 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ObjCPlugin.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#import <Cocoa/Cocoa.h> + + +@interface ObjCPlugin : NSObject +{ + BOOL throwOnDealloc; +} + +- (void)removeBridgeRestrictions:(id)container; + +@end diff --git a/WebKitTools/DumpRenderTree/mac/ObjCPlugin.m b/WebKitTools/DumpRenderTree/mac/ObjCPlugin.m new file mode 100644 index 0000000..3ec3e74 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ObjCPlugin.m @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 James G. Speth (speth@end.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#import "ObjCPlugin.h" +#import <objc/objc-runtime.h> +#import <WebKit/WebKit.h> + + +// === NSObject category to expose almost everything to JavaScript === + +// Warning: this class introduces huge security weaknesses, and should only be used +// for testing inside of DumpRenderTree, and only with trusted code. By default, it has +// the same restrictive behavior as the standard WebKit setup. However, scripts can use the +// plugin's removeBridgeRestrictions: method to open up almost total access to the Cocoa +// frameworks. + +static BOOL _allowsScriptsFullAccess = NO; + +@interface NSObject (ObjCScriptAccess) + ++ (void)setAllowsScriptsFullAccess:(BOOL)value; ++ (BOOL)allowsScriptsFullAccess; + +@end + +@implementation NSObject (ObjCScriptAccess) + ++ (void)setAllowsScriptsFullAccess:(BOOL)value +{ + _allowsScriptsFullAccess = value; +} + ++ (BOOL)allowsScriptsFullAccess +{ + return _allowsScriptsFullAccess; +} + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector +{ + return !_allowsScriptsFullAccess; +} + ++ (NSString *)webScriptNameForSelector:(SEL)selector +{ + return nil; +} + +@end + +@interface JSObjC : NSObject { +} + +// expose some useful objc functions to the scripting environment +- (id)lookUpClass:(NSString *)name; +- (void)log:(NSString *)message; +- (id)retainObject:(id)obj; +- (id)classOfObject:(id)obj; +- (NSString *)classNameOfObject:(id)obj; + +@end + +@implementation JSObjC + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector +{ + return NO; +} + ++ (NSString *)webScriptNameForSelector:(SEL)selector +{ + return nil; +} + +- (id)invokeDefaultMethodWithArguments:(NSArray *)args +{ + // this is a useful shortcut for accessing objective-c classes from the scripting + // environment, e.g. 'var myObject = objc("NSObject").alloc().init();' + if ([args count] == 1) + return [self lookUpClass:[args objectAtIndex:0]]; + return nil; +} + +- (id)lookUpClass:(NSString *)name +{ + return NSClassFromString(name); +} + +- (void)log:(NSString *)message +{ + NSLog(@"%@", message); +} + +- (id)retainObject:(id)obj +{ + return [obj retain]; +} + +- (id)classOfObject:(id)obj +{ + return (id)[obj class]; +} + +- (NSString *)classNameOfObject:(id)obj +{ + return [obj className]; +} + +@end + +@implementation ObjCPlugin + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + if (aSelector == @selector(removeBridgeRestrictions:)) + return NO; + + if (aSelector == @selector(echo:)) + return NO; + + if (aSelector == @selector(throwIfArgumentIsNotHello:)) + return NO; + + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(echo:)) + return @"echo"; + + if (aSelector == @selector(throwIfArgumentIsNotHello:)) + return @"throwIfArgumentIsNotHello"; + + return nil; +} + ++ (NSString *)webScriptNameForKey:(const char *)key +{ + if (strcmp(key, "throwOnDealloc") == 0) + return @"throwOnDealloc"; + + return nil; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)key +{ + if (strcmp(key, "throwOnDealloc") == 0) + return NO; + + return YES; +} + +- (void)removeBridgeRestrictions:(id)container +{ + // let scripts invoke any selector + [NSObject setAllowsScriptsFullAccess:YES]; + + // store a JSObjC instance into the provided container + JSObjC *objc = [[JSObjC alloc] init]; + [container setValue:objc forKey:@"objc"]; + [objc release]; +} + +- (id)echo:(id)obj +{ + return obj; +} + +- (void)throwIfArgumentIsNotHello:(NSString *)str +{ + if (![str isEqualToString:@"Hello"]) + [WebScriptObject throwException:[NSString stringWithFormat:@"%@ != Hello", str]]; +} + +- (void)dealloc +{ + if (throwOnDealloc) + [WebScriptObject throwException:@"Throwing exception on dealloc of ObjCPlugin"]; + + [super dealloc]; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/ObjCPluginFunction.h b/WebKitTools/DumpRenderTree/mac/ObjCPluginFunction.h new file mode 100644 index 0000000..1e81b21 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ObjCPluginFunction.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#import <Cocoa/Cocoa.h> + + +@interface ObjCPluginFunction : NSObject +{ +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/ObjCPluginFunction.m b/WebKitTools/DumpRenderTree/mac/ObjCPluginFunction.m new file mode 100644 index 0000000..5cd16f8 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ObjCPluginFunction.m @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#import "ObjCPluginFunction.h" + + +@implementation ObjCPluginFunction + +- (id)invokeDefaultMethodWithArguments:(NSArray *)args +{ + return @"test"; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/PixelDumpSupportMac.mm b/WebKitTools/DumpRenderTree/mac/PixelDumpSupportMac.mm new file mode 100644 index 0000000..d17c111 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/PixelDumpSupportMac.mm @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2007 Graham Dennis (graham.dennis@gmail.com) + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DumpRenderTree.h" +#include "PixelDumpSupport.h" +#include "PixelDumpSupportCG.h" + +#include "LayoutTestController.h" +#include <CoreGraphics/CGBitmapContext.h> +#ifndef BUILDING_ON_LEOPARD +#include <OpenGL/OpenGL.h> +#include <OpenGL/CGLMacro.h> +#endif +#include <wtf/Assertions.h> +#include <wtf/RefPtr.h> + +#import <WebKit/WebDocumentPrivate.h> +#import <WebKit/WebHTMLViewPrivate.h> +#import <WebKit/WebKit.h> +#import <WebKit/WebViewPrivate.h> + +// To ensure pixel tests consistency, we need to always render in the same colorspace. +// Unfortunately, because of AppKit / WebKit constraints, we can't render directly in the colorspace of our choice. +// This implies we have to temporarily change the profile of the main display to the colorspace we want to render into. +// We also need to make sure the CGBitmapContext we return is in that same colorspace. + +#define PROFILE_PATH "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc" // FIXME: This cannot be more than CS_MAX_PATH (256 characters) + +static CMProfileLocation sInitialProfileLocation; // The locType field is initialized to 0 which is the same as cmNoProfileBase + +void restoreMainDisplayColorProfile(int ignored) +{ + // This is used as a signal handler, and thus the calls into ColorSync are unsafe + // But we might as well try to restore the user's color profile, we're going down anyway... + if (sInitialProfileLocation.locType != cmNoProfileBase) { + const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost }; + int error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &sInitialProfileLocation); + if (error) + fprintf(stderr, "Failed to restore initial color profile for main display! Open System Preferences > Displays > Color and manually re-select the profile. (Error: %i)", error); + sInitialProfileLocation.locType = cmNoProfileBase; + } +} + +void setupMainDisplayColorProfile() +{ + const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost }; + int error; + + CMProfileRef profile = 0; + error = CMGetProfileByAVID((CMDisplayIDType)kCGDirectMainDisplay, &profile); + if (!error) { + UInt32 size = sizeof(CMProfileLocation); + error = NCMGetProfileLocation(profile, &sInitialProfileLocation, &size); + CMCloseProfile(profile); + } + if (error) { + fprintf(stderr, "Failed to retrieve current color profile for main display, thus it won't be changed. Many pixel tests may fail as a result. (Error: %i)", error); + sInitialProfileLocation.locType = cmNoProfileBase; + return; + } + + CMProfileLocation location; + location.locType = cmPathBasedProfile; + strcpy(location.u.pathLoc.path, PROFILE_PATH); + error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &location); + if (error) { + fprintf(stderr, "Failed to set color profile for main display! Many pixel tests may fail as a result. (Error: %i)", error); + sInitialProfileLocation.locType = cmNoProfileBase; + return; + } + + // Other signals are handled in installSignalHandlers() which also calls restoreMainDisplayColorProfile() + signal(SIGINT, restoreMainDisplayColorProfile); + signal(SIGHUP, restoreMainDisplayColorProfile); + signal(SIGTERM, restoreMainDisplayColorProfile); +} + +PassRefPtr<BitmapContext> createBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect) +{ + WebView* view = [mainFrame webView]; + + // If the WebHTMLView uses accelerated compositing, we need for force the on-screen capture path + // and also force Core Animation to start its animations with -display since the DRT window has autodisplay disabled. + if ([view _isUsingAcceleratedCompositing]) + onscreen = YES; + + NSSize webViewSize = [view frame].size; + size_t pixelsWide = static_cast<size_t>(webViewSize.width); + size_t pixelsHigh = static_cast<size_t>(webViewSize.height); + size_t rowBytes = (4 * pixelsWide + 63) & ~63; // Use a multiple of 64 bytes to improve CG performance + + void *buffer = calloc(pixelsHigh, rowBytes); + if (!buffer) + return 0; + + static CGColorSpaceRef colorSpace = 0; + if (!colorSpace) { + CMProfileLocation location; + location.locType = cmPathBasedProfile; + strcpy(location.u.pathLoc.path, PROFILE_PATH); + CMProfileRef profile; + if (CMOpenProfile(&profile, &location) == noErr) { + colorSpace = CGColorSpaceCreateWithPlatformColorSpace(profile); + CMCloseProfile(profile); + } + } + + CGContextRef context = CGBitmapContextCreate(buffer, pixelsWide, pixelsHigh, 8, rowBytes, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); // Use ARGB8 on PPC or BGRA8 on X86 to improve CG performance + if (!context) { + free(buffer); + return 0; + } + + // The BitmapContext keeps the CGContextRef and the pixel buffer alive + RefPtr<BitmapContext> bitmapContext = BitmapContext::createByAdoptingBitmapAndContext(buffer, context); + + NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]; + ASSERT(nsContext); + + if (incrementalRepaint) { + if (sweepHorizontally) { + for (NSRect column = NSMakeRect(0, 0, 1, webViewSize.height); column.origin.x < webViewSize.width; column.origin.x++) + [view displayRectIgnoringOpacity:column inContext:nsContext]; + } else { + for (NSRect line = NSMakeRect(0, 0, webViewSize.width, 1); line.origin.y < webViewSize.height; line.origin.y++) + [view displayRectIgnoringOpacity:line inContext:nsContext]; + } + } else { + if (onscreen) { +#ifdef BUILDING_ON_LEOPARD + // Ask the window server to provide us a composited version of the *real* window content including surfaces (i.e. OpenGL content) + // Note that the returned image might differ very slightly from the window backing because of dithering artifacts in the window server compositor + + CGImageRef image = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, [[view window] windowNumber], kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque); + CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); + CGImageRelease(image); +#else + // On 10.4 and earlier, we have to move the window temporarily "onscreen" and read directly from the display framebuffer using OpenGL + // In this code path, we need to ensure the window is above any other window or captured result will be corrupted + + NSWindow *window = [view window]; + int oldLevel = [window level]; + NSRect oldFrame = [window frame]; + + NSRect newFrame = [[[NSScreen screens] objectAtIndex:0] frame]; + newFrame = NSMakeRect(newFrame.origin.x + (newFrame.size.width - oldFrame.size.width) / 2, newFrame.origin.y + (newFrame.size.height - oldFrame.size.height) / 2, oldFrame.size.width, oldFrame.size.height); + [window setLevel:NSScreenSaverWindowLevel]; + [window setFrame:newFrame display:NO animate:NO]; + + CGRect rect = CGRectMake(newFrame.origin.x, newFrame.origin.y, webViewSize.width, webViewSize.height); + CGDirectDisplayID displayID; + CGDisplayCount count; + if (CGGetDisplaysWithRect(rect, 1, &displayID, &count) == kCGErrorSuccess) { + CGRect bounds = CGDisplayBounds(displayID); + rect.origin.x -= bounds.origin.x; + rect.origin.y -= bounds.origin.y; + + CGLPixelFormatAttribute attributes[] = {kCGLPFAAccelerated, kCGLPFANoRecovery, kCGLPFAFullScreen, kCGLPFADisplayMask, (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID), (CGLPixelFormatAttribute)0}; + CGLPixelFormatObj pixelFormat; + GLint num; + if (CGLChoosePixelFormat(attributes, &pixelFormat, &num) == kCGLNoError) { + CGLContextObj cgl_ctx; + if (CGLCreateContext(pixelFormat, 0, &cgl_ctx) == kCGLNoError) { + if (CGLSetFullScreen(cgl_ctx) == kCGLNoError) { + void *flipBuffer = calloc(pixelsHigh, rowBytes); + if (flipBuffer) { + glPixelStorei(GL_PACK_ROW_LENGTH, rowBytes / 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); +#if __BIG_ENDIAN__ + glReadPixels(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, flipBuffer); +#else + glReadPixels(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, flipBuffer); +#endif + if (!glGetError()) { + for(size_t i = 0; i < pixelsHigh; ++i) + bcopy((char*)flipBuffer + rowBytes * i, (char*)buffer + rowBytes * (pixelsHigh - i - 1), pixelsWide * 4); + } + + free(flipBuffer); + } + } + CGLDestroyContext(cgl_ctx); + } + CGLDestroyPixelFormat(pixelFormat); + } + } + + [window setFrame:oldFrame display:NO animate:NO]; + [window setLevel:oldLevel]; +#endif + } else { + // Grab directly the contents of the window backing buffer (this ignores any surfaces on the window) + // FIXME: This path is suboptimal: data is read from window backing store, converted to RGB8 then drawn again into an RGBA8 bitmap + + [view displayIfNeeded]; + [view lockFocus]; + NSBitmapImageRep *imageRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:[view frame]] autorelease]; + [view unlockFocus]; + + RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext]; + [NSGraphicsContext setCurrentContext:nsContext]; + [imageRep draw]; + [NSGraphicsContext setCurrentContext:savedContext.get()]; + } + } + + if (drawSelectionRect) { + NSView *documentView = [[mainFrame frameView] documentView]; + ASSERT([documentView conformsToProtocol:@protocol(WebDocumentSelection)]); + NSRect rect = [documentView convertRect:[(id <WebDocumentSelection>)documentView selectionRect] fromView:nil]; + CGContextSaveGState(context); + CGContextSetLineWidth(context, 1.0); + CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); + CGContextStrokeRect(context, CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)); + CGContextRestoreGState(context); + } + + return bitmapContext.release(); +} diff --git a/WebKitTools/DumpRenderTree/mac/PolicyDelegate.h b/WebKitTools/DumpRenderTree/mac/PolicyDelegate.h new file mode 100644 index 0000000..4cdf7c0 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/PolicyDelegate.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + + +@interface PolicyDelegate : NSObject { + BOOL permissiveDelegate; +} + +- (void)setPermissive:(BOOL)permissive; + +@end diff --git a/WebKitTools/DumpRenderTree/mac/PolicyDelegate.mm b/WebKitTools/DumpRenderTree/mac/PolicyDelegate.mm new file mode 100644 index 0000000..a0855d3 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/PolicyDelegate.mm @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "PolicyDelegate.h" + +#import "DumpRenderTree.h" +#import "DumpRenderTreeDraggingInfo.h" +#import <WebKit/WebPolicyDelegate.h> + +@implementation PolicyDelegate +- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation + request:(NSURLRequest *)request + frame:(WebFrame *)frame + decisionListener:(id<WebPolicyDecisionListener>)listener +{ + WebNavigationType navType = (WebNavigationType)[[actionInformation objectForKey:WebActionNavigationTypeKey] intValue]; + + const char* typeDescription; + switch (navType) { + case WebNavigationTypeLinkClicked: + typeDescription = "link clicked"; + break; + case WebNavigationTypeFormSubmitted: + typeDescription = "form submitted"; + break; + case WebNavigationTypeBackForward: + typeDescription = "back/forward"; + break; + case WebNavigationTypeReload: + typeDescription = "reload"; + break; + case WebNavigationTypeFormResubmitted: + typeDescription = "form resubmitted"; + break; + case WebNavigationTypeOther: + typeDescription = "other"; + break; + default: + typeDescription = "illegal value"; + } + + printf("Policy delegate: attempt to load %s with navigation type '%s'\n", [[[request URL] absoluteString] UTF8String], typeDescription); + + if (permissiveDelegate) + [listener use]; + else + [listener ignore]; +} + +- (void)setPermissive:(BOOL)permissive +{ + permissiveDelegate = permissive; +} + + +@end diff --git a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.h b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.h new file mode 100644 index 0000000..0c4618e --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + + +@interface ResourceLoadDelegate : NSObject { +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm new file mode 100644 index 0000000..2b7a8f3 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2007, Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "ResourceLoadDelegate.h" + +#import "DumpRenderTree.h" +#import "LayoutTestController.h" +#import <WebKit/WebKit.h> +#import <WebKit/WebTypesInternal.h> +#import <wtf/Assertions.h> + +@interface NSURL (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface NSError (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface NSURLResponse (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@interface NSURLRequest (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult; +@end + +@implementation NSError (DRTExtras) +- (NSString *)_drt_descriptionSuitableForTestResult +{ + NSString *str = [NSString stringWithFormat:@"<NSError domain %@, code %d", [self domain], [self code]]; + NSURL *failingURL; + + if ((failingURL = [[self userInfo] objectForKey:@"NSErrorFailingURLKey"])) + str = [str stringByAppendingFormat:@", failing URL \"%@\"", [failingURL _drt_descriptionSuitableForTestResult]]; + + str = [str stringByAppendingFormat:@">"]; + + return str; +} + +@end + +@implementation NSURL (DRTExtras) + +- (NSString *)_drt_descriptionSuitableForTestResult +{ + if (![self isFileURL]) + return [self absoluteString]; + + WebDataSource *dataSource = [mainFrame dataSource]; + if (!dataSource) + dataSource = [mainFrame provisionalDataSource]; + + NSString *basePath = [[[[dataSource request] URL] path] stringByDeletingLastPathComponent]; + + return [[self path] substringFromIndex:[basePath length] + 1]; +} + +@end + +@implementation NSURLResponse (DRTExtras) + +- (NSString *)_drt_descriptionSuitableForTestResult +{ + return [NSString stringWithFormat:@"<NSURLResponse %@>", [[self URL] _drt_descriptionSuitableForTestResult]]; +} + +@end + +@implementation NSURLRequest (DRTExtras) + +- (NSString *)_drt_descriptionSuitableForTestResult +{ + return [NSString stringWithFormat:@"<NSURLRequest URL %@, main document URL %@>", [[self URL] _drt_descriptionSuitableForTestResult], [[self mainDocumentURL] _drt_descriptionSuitableForTestResult]]; +} + +@end + +@implementation ResourceLoadDelegate + +- webView: (WebView *)wv identifierForInitialRequest: (NSURLRequest *)request fromDataSource: (WebDataSource *)dataSource +{ + ASSERT([[dataSource webFrame] dataSource] || [[dataSource webFrame] provisionalDataSource]); + + if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) + return [[request URL] _drt_descriptionSuitableForTestResult]; + + return @"<unknown>"; +} + +-(NSURLRequest *)webView: (WebView *)wv resource:identifier willSendRequest: (NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource +{ + if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - willSendRequest %@ redirectResponse %@", identifier, [newRequest _drt_descriptionSuitableForTestResult], + [redirectResponse _drt_descriptionSuitableForTestResult]]; + printf("%s\n", [string UTF8String]); + } + + NSURL *url = [newRequest URL]; + NSString *host = [url host]; + if (host + && (NSOrderedSame == [[url scheme] caseInsensitiveCompare:@"http"] || NSOrderedSame == [[url scheme] caseInsensitiveCompare:@"https"]) + && NSOrderedSame != [host compare:@"127.0.0.1"] + && NSOrderedSame != [host compare:@"255.255.255.255"] // used in some tests that expect to get back an error + && NSOrderedSame != [host caseInsensitiveCompare:@"localhost"]) { + printf("Blocked access to external URL %s\n", [[url absoluteString] cStringUsingEncoding:NSUTF8StringEncoding]); + return nil; + } + + if (disallowedURLs && CFSetContainsValue(disallowedURLs, url)) + return nil; + + return newRequest; +} + +- (void)webView:(WebView *)wv resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource +{ +} + +- (void)webView:(WebView *)wv resource:(id)identifier didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource +{ +} + +-(void)webView: (WebView *)wv resource:identifier didReceiveResponse: (NSURLResponse *)response fromDataSource:(WebDataSource *)dataSource +{ + if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didReceiveResponse %@", identifier, [response _drt_descriptionSuitableForTestResult]]; + printf("%s\n", [string UTF8String]); + } +} + +-(void)webView: (WebView *)wv resource:identifier didReceiveContentLength: (NSInteger)length fromDataSource:(WebDataSource *)dataSource +{ +} + +-(void)webView: (WebView *)wv resource:identifier didFinishLoadingFromDataSource:(WebDataSource *)dataSource +{ + if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didFinishLoading", identifier]; + printf("%s\n", [string UTF8String]); + } +} + +-(void)webView: (WebView *)wv resource:identifier didFailLoadingWithError:(NSError *)error fromDataSource:(WebDataSource *)dataSource +{ + if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - didFailLoadingWithError: %@", identifier, [error _drt_descriptionSuitableForTestResult]]; + printf("%s\n", [string UTF8String]); + } +} + +- (void)webView: (WebView *)wv plugInFailedWithError:(NSError *)error dataSource:(WebDataSource *)dataSource +{ + // The call to -display here simulates the "Plug-in not found" sheet that Safari shows. + // It is used for platform/mac/plugins/update-widget-from-style-recalc.html + [wv display]; +} + +-(NSCachedURLResponse *) webView: (WebView *)wv resource:(id)identifier willCacheResponse:(NSCachedURLResponse *)response fromDataSource:(WebDataSource *)dataSource +{ + if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { + NSString *string = [NSString stringWithFormat:@"%@ - willCacheResponse: called", identifier]; + printf("%s\n", [string UTF8String]); + } + return response; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/TextInputController.h b/WebKitTools/DumpRenderTree/mac/TextInputController.h new file mode 100644 index 0000000..767e72f --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/TextInputController.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +@class WebView; +@class WebHTMLView; +@class WebScriptObject; + +@interface TextInputController : NSObject +{ + WebView *webView; + WebHTMLView *inputMethodView; + WebScriptObject *inputMethodHandler; +} +- (id)initWithWebView:(WebView *)view; +@end diff --git a/WebKitTools/DumpRenderTree/mac/TextInputController.m b/WebKitTools/DumpRenderTree/mac/TextInputController.m new file mode 100644 index 0000000..a635070 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/TextInputController.m @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTreeMac.h" +#import "TextInputController.h" + +#import <AppKit/NSInputManager.h> +#import <WebKit/WebDocument.h> +#import <WebKit/WebFrame.h> +#import <WebKit/WebFrameView.h> +#import <WebKit/WebHTMLViewPrivate.h> +#import <WebKit/WebScriptObject.h> +#import <WebKit/WebTypesInternal.h> +#import <WebKit/WebView.h> + +@interface TextInputController (DumpRenderTreeInputMethodHandler) +- (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender; +@end + +@interface WebHTMLView (DumpRenderTreeInputMethodHandler) +- (void)interpretKeyEvents:(NSArray *)eventArray; +@end + +@interface WebHTMLView (WebKitSecretsTextInputControllerIsAwareOf) +- (WebFrame *)_frame; +@end + +@implementation WebHTMLView (DumpRenderTreeInputMethodHandler) +- (void)interpretKeyEvents:(NSArray *)eventArray +{ + WebScriptObject *obj = [[self _frame] windowObject]; + TextInputController *tic = [obj valueForKey:@"textInputController"]; + if (![tic interpretKeyEvents:eventArray withSender:self]) + [super interpretKeyEvents:eventArray]; +} +@end + +@implementation NSMutableAttributedString (TextInputController) + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + if (aSelector == @selector(string) + || aSelector == @selector(getLength) + || aSelector == @selector(attributeNamesAtIndex:) + || aSelector == @selector(valueOfAttribute:atIndex:) + || aSelector == @selector(addAttribute:value:) + || aSelector == @selector(addAttribute:value:from:length:) + || aSelector == @selector(addColorAttribute:red:green:blue:alpha:) + || aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:) + || aSelector == @selector(addFontAttribute:fontName:size:) + || aSelector == @selector(addFontAttribute:fontName:size:from:length:)) + return NO; + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(getLength)) + return @"length"; + if (aSelector == @selector(attributeNamesAtIndex:)) + return @"getAttributeNamesAtIndex"; + if (aSelector == @selector(valueOfAttribute:atIndex:)) + return @"getAttributeValueAtIndex"; + if (aSelector == @selector(addAttribute:value:)) + return @"addAttribute"; + if (aSelector == @selector(addAttribute:value:from:length:)) + return @"addAttributeForRange"; + if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:)) + return @"addColorAttribute"; + if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:)) + return @"addColorAttributeForRange"; + if (aSelector == @selector(addFontAttribute:fontName:size:)) + return @"addFontAttribute"; + if (aSelector == @selector(addFontAttribute:fontName:size:from:length:)) + return @"addFontAttributeForRange"; + + return nil; +} + +- (int)getLength +{ + return (int)[self length]; +} + +- (NSArray *)attributeNamesAtIndex:(int)index +{ + NSDictionary *attributes = [self attributesAtIndex:(unsigned)index effectiveRange:nil]; + return [attributes allKeys]; +} + +- (id)valueOfAttribute:(NSString *)attrName atIndex:(int)index +{ + return [self attribute:attrName atIndex:(unsigned)index effectiveRange:nil]; +} + +- (void)addAttribute:(NSString *)attrName value:(id)value +{ + [self addAttribute:attrName value:value range:NSMakeRange(0, [self length])]; +} + +- (void)addAttribute:(NSString *)attrName value:(id)value from:(int)from length:(int)length +{ + [self addAttribute:attrName value:value range:NSMakeRange((unsigned)from, (unsigned)length)]; +} + +- (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha +{ + [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange(0, [self length])]; +} + +- (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha from:(int)from length:(int)length +{ + [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange((unsigned)from, (unsigned)length)]; +} + +- (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize +{ + [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange(0, [self length])]; +} + +- (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize from:(int)from length:(int)length +{ + [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange((unsigned)from, (unsigned)length)]; +} + +@end + +@implementation TextInputController + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + if (aSelector == @selector(insertText:) + || aSelector == @selector(doCommand:) + || aSelector == @selector(setMarkedText:selectedFrom:length:) + || aSelector == @selector(unmarkText) + || aSelector == @selector(hasMarkedText) + || aSelector == @selector(conversationIdentifier) + || aSelector == @selector(substringFrom:length:) + || aSelector == @selector(attributedSubstringFrom:length:) + || aSelector == @selector(markedRange) + || aSelector == @selector(selectedRange) + || aSelector == @selector(firstRectForCharactersFrom:length:) + || aSelector == @selector(characterIndexForPointX:Y:) + || aSelector == @selector(validAttributesForMarkedText) + || aSelector == @selector(attributedStringWithString:) + || aSelector == @selector(setInputMethodHandler:)) + return NO; + return YES; +} + ++ (NSString *)webScriptNameForSelector:(SEL)aSelector +{ + if (aSelector == @selector(insertText:)) + return @"insertText"; + else if (aSelector == @selector(doCommand:)) + return @"doCommand"; + else if (aSelector == @selector(setMarkedText:selectedFrom:length:)) + return @"setMarkedText"; + else if (aSelector == @selector(substringFrom:length:)) + return @"substringFromRange"; + else if (aSelector == @selector(attributedSubstringFrom:length:)) + return @"attributedSubstringFromRange"; + else if (aSelector == @selector(firstRectForCharactersFrom:length:)) + return @"firstRectForCharacterRange"; + else if (aSelector == @selector(characterIndexForPointX:Y:)) + return @"characterIndexForPoint"; + else if (aSelector == @selector(attributedStringWithString:)) + return @"makeAttributedString"; // just a factory method, doesn't call into NSTextInput + else if (aSelector == @selector(setInputMethodHandler:)) + return @"setInputMethodHandler"; + + return nil; +} + +- (id)initWithWebView:(WebView *)wv +{ + self = [super init]; + webView = wv; + inputMethodView = nil; + inputMethodHandler = nil; + return self; +} + +- (void)dealloc +{ + [inputMethodHandler release]; + inputMethodHandler = nil; + + [super dealloc]; +} + +- (NSObject <NSTextInput> *)textInput +{ + NSView <NSTextInput> *view = inputMethodView ? inputMethodView : (id)[[[webView mainFrame] frameView] documentView]; + return [view conformsToProtocol:@protocol(NSTextInput)] ? view : nil; +} + +- (void)insertText:(id)aString +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + [textInput insertText:aString]; +} + +- (void)doCommand:(NSString *)aCommand +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + [textInput doCommandBySelector:NSSelectorFromString(aCommand)]; +} + +- (void)setMarkedText:(NSString *)aString selectedFrom:(int)from length:(int)length +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + [textInput setMarkedText:aString selectedRange:NSMakeRange(from, length)]; +} + +- (void)unmarkText +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + [textInput unmarkText]; +} + +- (BOOL)hasMarkedText +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + return [textInput hasMarkedText]; + + return FALSE; +} + +- (long)conversationIdentifier +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + return [textInput conversationIdentifier]; + + return 0; +} + +- (NSString *)substringFrom:(int)from length:(int)length +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + return [[textInput attributedSubstringFromRange:NSMakeRange(from, length)] string]; + + return @""; +} + +- (NSMutableAttributedString *)attributedSubstringFrom:(int)from length:(int)length +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + NSMutableAttributedString *ret = [[[NSMutableAttributedString alloc] init] autorelease]; + + if (textInput) + [ret setAttributedString:[textInput attributedSubstringFromRange:NSMakeRange(from, length)]]; + + return ret; +} + +- (NSArray *)markedRange +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) { + NSRange range = [textInput markedRange]; + return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil]; + } + + return nil; +} + +- (NSArray *)selectedRange +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) { + NSRange range = [textInput selectedRange]; + return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil]; + } + + return nil; +} + + +- (NSArray *)firstRectForCharactersFrom:(int)from length:(int)length +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) { + NSRect rect = [textInput firstRectForCharacterRange:NSMakeRange(from, length)]; + if (rect.origin.x || rect.origin.y || rect.size.width || rect.size.height) { + rect.origin = [[webView window] convertScreenToBase:rect.origin]; + rect = [webView convertRect:rect fromView:nil]; + } + return [NSArray arrayWithObjects: + [NSNumber numberWithFloat:rect.origin.x], + [NSNumber numberWithFloat:rect.origin.y], + [NSNumber numberWithFloat:rect.size.width], + [NSNumber numberWithFloat:rect.size.height], + nil]; + } + + return nil; +} + +- (NSInteger)characterIndexForPointX:(float)x Y:(float)y +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) { + NSPoint point = NSMakePoint(x, y); + point = [webView convertPoint:point toView:nil]; + point = [[webView window] convertBaseToScreen:point]; + NSInteger index = [textInput characterIndexForPoint:point]; + if (index == NSNotFound) + return -1; + + return index; + } + + return 0; +} + +- (NSArray *)validAttributesForMarkedText +{ + NSObject <NSTextInput> *textInput = [self textInput]; + + if (textInput) + return [textInput validAttributesForMarkedText]; + + return nil; +} + +- (NSMutableAttributedString *)attributedStringWithString:(NSString *)aString +{ + return [[[NSMutableAttributedString alloc] initWithString:aString] autorelease]; +} + +- (void)setInputMethodHandler:(WebScriptObject *)handler +{ + if (inputMethodHandler == handler) + return; + [handler retain]; + [inputMethodHandler release]; + inputMethodHandler = handler; +} + +- (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender +{ + if (!inputMethodHandler) + return NO; + + inputMethodView = sender; + + NSEvent *event = [eventArray objectAtIndex:0]; + unsigned modifierFlags = [event modifierFlags]; + NSMutableArray *modifiers = [[NSMutableArray alloc] init]; + if (modifierFlags & NSAlphaShiftKeyMask) + [modifiers addObject:@"NSAlphaShiftKeyMask"]; + if (modifierFlags & NSShiftKeyMask) + [modifiers addObject:@"NSShiftKeyMask"]; + if (modifierFlags & NSControlKeyMask) + [modifiers addObject:@"NSControlKeyMask"]; + if (modifierFlags & NSAlternateKeyMask) + [modifiers addObject:@"NSAlternateKeyMask"]; + if (modifierFlags & NSCommandKeyMask) + [modifiers addObject:@"NSCommandKeyMask"]; + if (modifierFlags & NSNumericPadKeyMask) + [modifiers addObject:@"NSNumericPadKeyMask"]; + if (modifierFlags & NSHelpKeyMask) + [modifiers addObject:@"NSHelpKeyMask"]; + if (modifierFlags & NSFunctionKeyMask) + [modifiers addObject:@"NSFunctionKeyMask"]; + + WebScriptObject* eventParam = [inputMethodHandler evaluateWebScript:@"new Object();"]; + [eventParam setValue:[event characters] forKey:@"characters"]; + [eventParam setValue:[event charactersIgnoringModifiers] forKey:@"charactersIgnoringModifiers"]; + [eventParam setValue:[NSNumber numberWithBool:[event isARepeat]] forKey:@"isARepeat"]; + [eventParam setValue:[NSNumber numberWithUnsignedShort:[event keyCode]] forKey:@"keyCode"]; + [eventParam setValue:modifiers forKey:@"modifierFlags"]; + + [modifiers release]; + + id result = [inputMethodHandler callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:inputMethodHandler, eventParam, nil]]; + if (![result respondsToSelector:@selector(boolValue)] || ![result boolValue]) + [sender doCommandBySelector:@selector(noop:)]; // AppKit sends noop: if the ime does not handle an event + + inputMethodView = nil; + return YES; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/UIDelegate.h b/WebKitTools/DumpRenderTree/mac/UIDelegate.h new file mode 100644 index 0000000..da472d6 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/UIDelegate.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +@interface UIDelegate : NSObject { + +@private + NSRect m_frame; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/UIDelegate.mm b/WebKitTools/DumpRenderTree/mac/UIDelegate.mm new file mode 100644 index 0000000..550892f --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/UIDelegate.mm @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2006. 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" +#import "UIDelegate.h" + +#import "DumpRenderTreeDraggingInfo.h" +#import "EventSendingController.h" +#import "LayoutTestController.h" +#import <WebKit/WebFramePrivate.h> +#import <WebKit/WebHTMLViewPrivate.h> +#import <WebKit/WebView.h> +#import <WebKit/WebSecurityOriginPrivate.h> +#import <wtf/Assertions.h> + +DumpRenderTreeDraggingInfo *draggingInfo = nil; + +@implementation UIDelegate + +- (void)webView:(WebView *)sender setFrame:(NSRect)frame +{ + m_frame = frame; +} + +- (NSRect)webViewFrame:(WebView *)sender +{ + return m_frame; +} + +- (void)webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary +{ + NSString *message = [dictionary objectForKey:@"message"]; + NSNumber *lineNumber = [dictionary objectForKey:@"lineNumber"]; + + NSRange range = [message rangeOfString:@"file://"]; + if (range.location != NSNotFound) + message = [[message substringToIndex:range.location] stringByAppendingString:[[message substringFromIndex:NSMaxRange(range)] lastPathComponent]]; + + printf ("CONSOLE MESSAGE: line %d: %s\n", [lineNumber intValue], [message UTF8String]); +} + +- (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame +{ + if (!done) + printf("ALERT: %s\n", [message UTF8String]); +} + +- (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame +{ + if (!done) + printf("CONFIRM: %s\n", [message UTF8String]); + return YES; +} + +- (NSString *)webView:(WebView *)sender runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WebFrame *)frame +{ + if (!done) + printf("PROMPT: %s, default text: %s\n", [prompt UTF8String], [defaultText UTF8String]); + return defaultText; +} + +- (BOOL)webView:(WebView *)c runBeforeUnloadConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame +{ + if (!done) + printf("CONFIRM NAVIGATION: %s\n", [message UTF8String]); + return YES; +} + + +- (void)webView:(WebView *)sender dragImage:(NSImage *)anImage at:(NSPoint)viewLocation offset:(NSSize)initialOffset event:(NSEvent *)event pasteboard:(NSPasteboard *)pboard source:(id)sourceObj slideBack:(BOOL)slideFlag forView:(NSView *)view +{ + assert(!draggingInfo); + if (gLayoutTestController->addFileToPasteboardOnDrag()) { + [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; + [pboard setPropertyList:[NSArray arrayWithObject:@"DRTFakeFile"] forType:NSFilenamesPboardType]; + } + draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:anImage offset:initialOffset pasteboard:pboard source:sourceObj]; + [EventSendingController replaySavedEvents]; +} + +- (void)webViewFocus:(WebView *)webView +{ + gLayoutTestController->setWindowIsKey(true); +} + +- (void)webViewUnfocus:(WebView *)webView +{ + gLayoutTestController->setWindowIsKey(false); +} + +- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request +{ + if (!gLayoutTestController->canOpenWindows()) + return nil; + + // Make sure that waitUntilDone has been called. + ASSERT(gLayoutTestController->waitToDump()); + + WebView *webView = createWebViewAndOffscreenWindow(); + + return [webView autorelease]; +} + +- (void)webViewClose:(WebView *)sender +{ + NSWindow* window = [sender window]; + + if (gLayoutTestController->callCloseOnWebViews()) + [sender close]; + + [window close]; +} + +- (void)webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)databaseIdentifier +{ + if (!done && gLayoutTestController->dumpDatabaseCallbacks()) + printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n", [[origin protocol] UTF8String], [[origin host] UTF8String], + [origin port], [databaseIdentifier UTF8String]); + + static const unsigned long long defaultQuota = 5 * 1024 * 1024; + [origin setQuota:defaultQuota]; +} + +- (void)webView:(WebView *)sender setStatusText:(NSString *)text +{ + if (gLayoutTestController->dumpStatusCallbacks()) + printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", [text UTF8String]); +} + +- (void)dealloc +{ + [draggingInfo release]; + draggingInfo = nil; + + [super dealloc]; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/WorkQueueItemMac.mm b/WebKitTools/DumpRenderTree/mac/WorkQueueItemMac.mm new file mode 100644 index 0000000..3a7b0e2 --- /dev/null +++ b/WebKitTools/DumpRenderTree/mac/WorkQueueItemMac.mm @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DumpRenderTree.h" +#import "WorkQueueItem.h" + +#import <JavaScriptCore/JSStringRef.h> +#import <JavaScriptCore/JSStringRefCF.h> +#import <WebKit/WebBackForwardList.h> +#import <WebKit/WebFrame.h> +#import <WebKit/WebScriptObject.h> +#import <WebKit/WebView.h> +#import <wtf/RetainPtr.h> + +void LoadItem::invoke() const +{ + RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, m_url.get())); + NSString *urlNS = (NSString *)urlCF.get(); + RetainPtr<CFStringRef> targetCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, m_target.get())); + NSString *targetNS = (NSString *)targetCF.get(); + + WebFrame *targetFrame; + if (targetNS && [targetNS length]) + targetFrame = [mainFrame findFrameNamed:targetNS]; + else + targetFrame = mainFrame; + [targetFrame loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlNS]]]; +} + +void ReloadItem::invoke() const +{ + [[mainFrame webView] reload:nil]; +} + +void ScriptItem::invoke() const +{ + RetainPtr<CFStringRef> scriptCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, m_script.get())); + NSString *scriptNS = (NSString *)scriptCF.get(); + [[mainFrame webView] stringByEvaluatingJavaScriptFromString:scriptNS]; +} + +void BackForwardItem::invoke() const +{ + if (m_howFar == 1) + [[mainFrame webView] goForward]; + else if (m_howFar == -1) + [[mainFrame webView] goBack]; + else { + WebBackForwardList *bfList = [[mainFrame webView] backForwardList]; + [[mainFrame webView] goToBackForwardItem:[bfList itemAtIndex:m_howFar]]; + } +} |