--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKitTools/DumpRenderTree/mac/EventSendingController.mm Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, 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/WebKit.h>
+#import <WebKit/DOMPrivate.h>
+
+extern "C" void _NSNewKillRingSequence();
+
+NSPoint lastMousePosition;
+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(mouseMoveToX:Y:)
+ || aSelector == @selector(leapForward:)
+ || aSelector == @selector(keyDown:withModifiers:)
+ || aSelector == @selector(enableDOMUIEventLogging:)
+ || aSelector == @selector(fireKeyboardEventsToElement:)
+ || aSelector == @selector(clearKillRing)
+ || aSelector == @selector(textZoomIn)
+ || aSelector == @selector(textZoomOut))
+ 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(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 && down && !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();
+}
+
+- (void)mouseDown
+{
+ [[[mainFrame frameView] documentView] layout];
+ if ([self currentEventTime] - lastClick >= 1)
+ clickCount = 1;
+ else
+ clickCount++;
+ NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseDown
+ 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];
+ down = YES;
+ }
+}
+
+- (void)textZoomIn
+{
+ [[mainFrame webView] makeTextLarger:self];
+}
+
+- (void)textZoomOut
+{
+ [[mainFrame webView] makeTextSmaller:self];
+}
+
+- (void)mouseUp
+{
+ if (dragMode && !replayingSavedEvents) {
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp)]];
+ [invocation setTarget:self];
+ [invocation setSelector:@selector(mouseUp)];
+
+ [EventSendingController saveEvent:invocation];
+ [EventSendingController replaySavedEvents];
+
+ return;
+ }
+
+ [[[mainFrame frameView] documentView] layout];
+ NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseUp
+ 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];
+ down = NO;
+ lastClick = [event timestamp];
+ 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 && down && !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:(down ? NSLeftMouseDragged : NSMouseMoved)
+ location:lastMousePosition
+ modifierFlags:0
+ timestamp:[self currentEventTime]
+ windowNumber:[[view window] windowNumber]
+ context:[NSGraphicsContext currentContext]
+ eventNumber:++eventNumber
+ clickCount:(down ? clickCount : 0)
+ pressure:0.0];
+
+ NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
+ if (subView) {
+ if (down) {
+ [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];
+ if ([self currentEventTime] - lastClick >= 1)
+ clickCount = 1;
+ else
+ clickCount++;
+ 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)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 *modifier = nil;
+ int mask = 0;
+
+ if ([modifiers isKindOfClass:[WebScriptObject class]])
+ for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
+ modifier = (NSString *)[modifiers webScriptValueAtIndex:i];
+ if ([modifier isEqual:@"ctrlKey"])
+ mask |= NSControlKeyMask;
+ else if ([modifier isEqual:@"shiftKey"])
+ mask |= NSShiftKeyMask;
+ else if ([modifier isEqual:@"altKey"])
+ mask |= NSAlternateKeyMask;
+ else if ([modifier isEqual:@"metaKey"])
+ mask |= NSCommandKeyMask;
+ else
+ break;
+ }
+
+ [[[mainFrame frameView] documentView] layout];
+
+ NSEvent *event = [NSEvent keyEventWithType:NSKeyDown
+ location:NSMakePoint(5, 5)
+ modifierFlags:mask
+ timestamp:[self currentEventTime]
+ windowNumber:[[[mainFrame webView] window] windowNumber]
+ context:[NSGraphicsContext currentContext]
+ characters:character
+ charactersIgnoringModifiers:character
+ isARepeat:NO
+ keyCode:0];
+
+
+ NSResponder *firstResponder = [[[mainFrame webView] window] firstResponder];
+ [firstResponder keyDown: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