Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  * Copyright (C) 2006 Jonas Witt <jonas.witt (at) gmail.com>
      4  * Copyright (C) 2006 Samuel Weinig <sam.weinig (at) gmail.com>
      5  * Copyright (C) 2006 Alexey Proskuryakov <ap (at) nypop.com>
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  *
     11  * 1.  Redistributions of source code must retain the above copyright
     12  *     notice, this list of conditions and the following disclaimer.
     13  * 2.  Redistributions in binary form must reproduce the above copyright
     14  *     notice, this list of conditions and the following disclaimer in the
     15  *     documentation and/or other materials provided with the distribution.
     16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     17  *     its contributors may be used to endorse or promote products derived
     18  *     from this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #import "config.h"
     33 #import "EventSendingController.h"
     34 
     35 #import "DumpRenderTree.h"
     36 #import "DumpRenderTreeDraggingInfo.h"
     37 #import "DumpRenderTreeFileDraggingSource.h"
     38 
     39 #import <Carbon/Carbon.h>                           // for GetCurrentEventTime()
     40 #import <WebKit/DOMPrivate.h>
     41 #import <WebKit/WebKit.h>
     42 #import <WebKit/WebViewPrivate.h>
     43 
     44 extern "C" void _NSNewKillRingSequence();
     45 
     46 enum MouseAction {
     47     MouseDown,
     48     MouseUp,
     49     MouseDragged
     50 };
     51 
     52 // Match the DOM spec (sadly the DOM spec does not provide an enum)
     53 enum MouseButton {
     54     LeftMouseButton = 0,
     55     MiddleMouseButton = 1,
     56     RightMouseButton = 2,
     57     NoMouseButton = -1
     58 };
     59 
     60 NSPoint lastMousePosition;
     61 NSPoint lastClickPosition;
     62 int lastClickButton = NoMouseButton;
     63 NSArray *webkitDomEventNames;
     64 NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once.
     65 BOOL replayingSavedEvents;
     66 
     67 @implementation EventSendingController
     68 
     69 + (void)initialize
     70 {
     71     webkitDomEventNames = [[NSArray alloc] initWithObjects:
     72         @"abort",
     73         @"beforecopy",
     74         @"beforecut",
     75         @"beforepaste",
     76         @"blur",
     77         @"change",
     78         @"click",
     79         @"contextmenu",
     80         @"copy",
     81         @"cut",
     82         @"dblclick",
     83         @"drag",
     84         @"dragend",
     85         @"dragenter",
     86         @"dragleave",
     87         @"dragover",
     88         @"dragstart",
     89         @"drop",
     90         @"error",
     91         @"focus",
     92         @"input",
     93         @"keydown",
     94         @"keypress",
     95         @"keyup",
     96         @"load",
     97         @"mousedown",
     98         @"mousemove",
     99         @"mouseout",
    100         @"mouseover",
    101         @"mouseup",
    102         @"mousewheel",
    103         @"beforeunload",
    104         @"paste",
    105         @"readystatechange",
    106         @"reset",
    107         @"resize",
    108         @"scroll",
    109         @"search",
    110         @"select",
    111         @"selectstart",
    112         @"submit",
    113         @"textInput",
    114         @"textzoomin",
    115         @"textzoomout",
    116         @"unload",
    117         @"zoom",
    118         nil];
    119 }
    120 
    121 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
    122 {
    123     if (aSelector == @selector(beginDragWithFiles:)
    124             || aSelector == @selector(clearKillRing)
    125             || aSelector == @selector(contextClick)
    126             || aSelector == @selector(enableDOMUIEventLogging:)
    127             || aSelector == @selector(fireKeyboardEventsToElement:)
    128             || aSelector == @selector(keyDown:withModifiers:withLocation:)
    129             || aSelector == @selector(leapForward:)
    130             || aSelector == @selector(mouseDown:withModifiers:)
    131             || aSelector == @selector(mouseMoveToX:Y:)
    132             || aSelector == @selector(mouseUp:withModifiers:)
    133             || aSelector == @selector(scheduleAsynchronousClick)
    134             || aSelector == @selector(textZoomIn)
    135             || aSelector == @selector(textZoomOut)
    136             || aSelector == @selector(zoomPageIn)
    137             || aSelector == @selector(zoomPageOut))
    138         return NO;
    139     return YES;
    140 }
    141 
    142 + (BOOL)isKeyExcludedFromWebScript:(const char*)name
    143 {
    144     if (strcmp(name, "dragMode") == 0)
    145         return NO;
    146     return YES;
    147 }
    148 
    149 + (NSString *)webScriptNameForSelector:(SEL)aSelector
    150 {
    151     if (aSelector == @selector(beginDragWithFiles:))
    152         return @"beginDragWithFiles";
    153     if (aSelector == @selector(enableDOMUIEventLogging:))
    154         return @"enableDOMUIEventLogging";
    155     if (aSelector == @selector(fireKeyboardEventsToElement:))
    156         return @"fireKeyboardEventsToElement";
    157     if (aSelector == @selector(keyDown:withModifiers:withLocation:))
    158         return @"keyDown";
    159     if (aSelector == @selector(leapForward:))
    160         return @"leapForward";
    161     if (aSelector == @selector(mouseDown:withModifiers:))
    162         return @"mouseDown";
    163     if (aSelector == @selector(mouseUp:withModifiers:))
    164         return @"mouseUp";
    165     if (aSelector == @selector(mouseMoveToX:Y:))
    166         return @"mouseMoveTo";
    167     if (aSelector == @selector(setDragMode:))
    168         return @"setDragMode";
    169     return nil;
    170 }
    171 
    172 - (id)init
    173 {
    174     self = [super init];
    175     if (self)
    176         dragMode = YES;
    177     return self;
    178 }
    179 
    180 - (void)dealloc
    181 {
    182     [super dealloc];
    183 }
    184 
    185 - (double)currentEventTime
    186 {
    187     return GetCurrentEventTime() + timeOffset;
    188 }
    189 
    190 - (void)leapForward:(int)milliseconds
    191 {
    192     if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
    193         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]];
    194         [invocation setTarget:self];
    195         [invocation setSelector:@selector(leapForward:)];
    196         [invocation setArgument:&milliseconds atIndex:2];
    197 
    198         [EventSendingController saveEvent:invocation];
    199 
    200         return;
    201     }
    202 
    203     timeOffset += milliseconds / 1000.0;
    204 }
    205 
    206 - (void)clearKillRing
    207 {
    208     _NSNewKillRingSequence();
    209 }
    210 
    211 static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action)
    212 {
    213     switch (button) {
    214         case LeftMouseButton:
    215             switch (action) {
    216                 case MouseDown:
    217                     return NSLeftMouseDown;
    218                 case MouseUp:
    219                     return NSLeftMouseUp;
    220                 case MouseDragged:
    221                     return NSLeftMouseDragged;
    222             }
    223         case RightMouseButton:
    224             switch (action) {
    225                 case MouseDown:
    226                     return NSRightMouseDown;
    227                 case MouseUp:
    228                     return NSRightMouseUp;
    229                 case MouseDragged:
    230                     return NSRightMouseDragged;
    231             }
    232         default:
    233             switch (action) {
    234                 case MouseDown:
    235                     return NSOtherMouseDown;
    236                 case MouseUp:
    237                     return NSOtherMouseUp;
    238                 case MouseDragged:
    239                     return NSOtherMouseDragged;
    240             }
    241     }
    242     assert(0);
    243     return static_cast<NSEventType>(0);
    244 }
    245 
    246 - (void)beginDragWithFiles:(WebScriptObject*)jsFilePaths
    247 {
    248     assert(!draggingInfo);
    249     assert([jsFilePaths isKindOfClass:[WebScriptObject class]]);
    250 
    251     NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName];
    252     [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
    253 
    254     NSURL *currentTestURL = [NSURL URLWithString:[[mainFrame webView] mainFrameURL]];
    255 
    256     NSMutableArray *filePaths = [NSMutableArray array];
    257     for (unsigned i = 0; [[jsFilePaths webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
    258         NSString *filePath = (NSString *)[jsFilePaths webScriptValueAtIndex:i];
    259         // Have NSURL encode the name so that we handle '?' in file names correctly.
    260         NSURL *fileURL = [NSURL fileURLWithPath:filePath];
    261         NSURL *absoluteFileURL = [NSURL URLWithString:[fileURL relativeString]  relativeToURL:currentTestURL];
    262         [filePaths addObject:[absoluteFileURL path]];
    263     }
    264 
    265     [pboard setPropertyList:filePaths forType:NSFilenamesPboardType];
    266     assert([pboard propertyListForType:NSFilenamesPboardType]); // setPropertyList will silently fail on error, assert that it didn't fail
    267 
    268     // Provide a source, otherwise [DumpRenderTreeDraggingInfo draggingSourceOperationMask] defaults to NSDragOperationNone
    269     DumpRenderTreeFileDraggingSource *source = [[[DumpRenderTreeFileDraggingSource alloc] init] autorelease];
    270     draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pboard source:source];
    271     [[mainFrame webView] draggingEntered:draggingInfo];
    272 
    273     dragMode = NO; // dragMode saves events and then replays them later.  We don't need/want that.
    274     leftMouseButtonDown = YES; // Make the rest of eventSender think a drag is in progress
    275 }
    276 
    277 - (void)updateClickCountForButton:(int)buttonNumber
    278 {
    279     if (([self currentEventTime] - lastClick >= 1) ||
    280         !NSEqualPoints(lastMousePosition, lastClickPosition) ||
    281         lastClickButton != buttonNumber) {
    282         clickCount = 1;
    283         lastClickButton = buttonNumber;
    284     } else
    285         clickCount++;
    286 }
    287 
    288 static int buildModifierFlags(const WebScriptObject* modifiers)
    289 {
    290     int flags = 0;
    291     if (![modifiers isKindOfClass:[WebScriptObject class]])
    292         return flags;
    293     for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
    294         NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i];
    295         if ([modifierName isEqual:@"ctrlKey"])
    296             flags |= NSControlKeyMask;
    297         else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"])
    298             flags |= NSShiftKeyMask;
    299         else if ([modifierName isEqual:@"altKey"])
    300             flags |= NSAlternateKeyMask;
    301         else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"])
    302             flags |= NSCommandKeyMask;
    303     }
    304     return flags;
    305 }
    306 
    307 - (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
    308 {
    309     [[[mainFrame frameView] documentView] layout];
    310     [self updateClickCountForButton:buttonNumber];
    311 
    312     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
    313     NSEvent *event = [NSEvent mouseEventWithType:eventType
    314                                         location:lastMousePosition
    315                                    modifierFlags:buildModifierFlags(modifiers)
    316                                        timestamp:[self currentEventTime]
    317                                     windowNumber:[[[mainFrame webView] window] windowNumber]
    318                                          context:[NSGraphicsContext currentContext]
    319                                      eventNumber:++eventNumber
    320                                       clickCount:clickCount
    321                                         pressure:0.0];
    322 
    323     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
    324     if (subView) {
    325         [subView mouseDown:event];
    326         if (buttonNumber == LeftMouseButton)
    327             leftMouseButtonDown = YES;
    328     }
    329 }
    330 
    331 - (void)mouseDown:(int)buttonNumber
    332 {
    333     [self mouseDown:buttonNumber withModifiers:nil];
    334 }
    335 
    336 - (void)textZoomIn
    337 {
    338     [[mainFrame webView] makeTextLarger:self];
    339 }
    340 
    341 - (void)textZoomOut
    342 {
    343     [[mainFrame webView] makeTextSmaller:self];
    344 }
    345 
    346 - (void)zoomPageIn
    347 {
    348     [[mainFrame webView] zoomPageIn:self];
    349 }
    350 
    351 - (void)zoomPageOut
    352 {
    353     [[mainFrame webView] zoomPageOut:self];
    354 }
    355 
    356 - (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
    357 {
    358     if (dragMode && !replayingSavedEvents) {
    359         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]];
    360         [invocation setTarget:self];
    361         [invocation setSelector:@selector(mouseUp:withModifiers:)];
    362         [invocation setArgument:&buttonNumber atIndex:2];
    363         [invocation setArgument:&modifiers atIndex:3];
    364 
    365         [EventSendingController saveEvent:invocation];
    366         [EventSendingController replaySavedEvents];
    367 
    368         return;
    369     }
    370 
    371     [[[mainFrame frameView] documentView] layout];
    372     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
    373     NSEvent *event = [NSEvent mouseEventWithType:eventType
    374                                         location:lastMousePosition
    375                                    modifierFlags:buildModifierFlags(modifiers)
    376                                        timestamp:[self currentEventTime]
    377                                     windowNumber:[[[mainFrame webView] window] windowNumber]
    378                                          context:[NSGraphicsContext currentContext]
    379                                      eventNumber:++eventNumber
    380                                       clickCount:clickCount
    381                                         pressure:0.0];
    382 
    383     NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]];
    384     // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView.
    385     // The right solution is just to use NSApplication's built-in event sending methods,
    386     // instead of rolling our own algorithm for selecting an event target.
    387     targetView = targetView ? targetView : [[mainFrame frameView] documentView];
    388     assert(targetView);
    389     [targetView mouseUp:event];
    390     if (buttonNumber == LeftMouseButton)
    391         leftMouseButtonDown = NO;
    392     lastClick = [event timestamp];
    393     lastClickPosition = lastMousePosition;
    394     if (draggingInfo) {
    395         WebView *webView = [mainFrame webView];
    396 
    397         NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo];
    398 
    399         if (dragOperation != NSDragOperationNone)
    400             [webView performDragOperation:draggingInfo];
    401         else
    402             [webView draggingExited:draggingInfo];
    403         // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation:
    404         if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)])
    405             [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation];
    406         [draggingInfo release];
    407         draggingInfo = nil;
    408     }
    409 }
    410 
    411 - (void)mouseUp:(int)buttonNumber
    412 {
    413     [self mouseUp:buttonNumber withModifiers:nil];
    414 }
    415 
    416 - (void)mouseMoveToX:(int)x Y:(int)y
    417 {
    418     if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
    419         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]];
    420         [invocation setTarget:self];
    421         [invocation setSelector:@selector(mouseMoveToX:Y:)];
    422         [invocation setArgument:&x atIndex:2];
    423         [invocation setArgument:&y atIndex:3];
    424 
    425         [EventSendingController saveEvent:invocation];
    426         return;
    427     }
    428 
    429     NSView *view = [mainFrame webView];
    430     lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil];
    431     NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved)
    432                                         location:lastMousePosition
    433                                    modifierFlags:0
    434                                        timestamp:[self currentEventTime]
    435                                     windowNumber:[[view window] windowNumber]
    436                                          context:[NSGraphicsContext currentContext]
    437                                      eventNumber:++eventNumber
    438                                       clickCount:(leftMouseButtonDown ? clickCount : 0)
    439                                         pressure:0.0];
    440 
    441     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
    442     if (subView) {
    443         if (leftMouseButtonDown) {
    444             [subView mouseDragged:event];
    445             if (draggingInfo) {
    446                 // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo:
    447                 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)])
    448                     [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition];
    449                 [[mainFrame webView] draggingUpdated:draggingInfo];
    450             }
    451         } else
    452             [subView mouseMoved:event];
    453     }
    454 }
    455 
    456 - (void)contextClick
    457 {
    458     [[[mainFrame frameView] documentView] layout];
    459     [self updateClickCountForButton:RightMouseButton];
    460 
    461     NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown
    462                                         location:lastMousePosition
    463                                    modifierFlags:0
    464                                        timestamp:[self currentEventTime]
    465                                     windowNumber:[[[mainFrame webView] window] windowNumber]
    466                                          context:[NSGraphicsContext currentContext]
    467                                      eventNumber:++eventNumber
    468                                       clickCount:clickCount
    469                                         pressure:0.0];
    470 
    471     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
    472     if (subView)
    473         [subView menuForEvent:event];
    474 }
    475 
    476 - (void)scheduleAsynchronousClick
    477 {
    478     [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0];
    479     [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0];
    480 }
    481 
    482 + (void)saveEvent:(NSInvocation *)event
    483 {
    484     if (!savedMouseEvents)
    485         savedMouseEvents = [[NSMutableArray alloc] init];
    486     [savedMouseEvents addObject:event];
    487 }
    488 
    489 + (void)replaySavedEvents
    490 {
    491     replayingSavedEvents = YES;
    492     while ([savedMouseEvents count]) {
    493         // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate
    494         NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease];
    495         [savedMouseEvents removeObjectAtIndex:0];
    496         [invocation invoke];
    497     }
    498     replayingSavedEvents = NO;
    499 }
    500 
    501 + (void)clearSavedEvents
    502 {
    503     [savedMouseEvents release];
    504     savedMouseEvents = nil;
    505 }
    506 
    507 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)keyLocation
    508 {
    509     NSString *eventCharacter = character;
    510     if ([character isEqualToString:@"leftArrow"]) {
    511         const unichar ch = NSLeftArrowFunctionKey;
    512         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    513     } else if ([character isEqualToString:@"rightArrow"]) {
    514         const unichar ch = NSRightArrowFunctionKey;
    515         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    516     } else if ([character isEqualToString:@"upArrow"]) {
    517         const unichar ch = NSUpArrowFunctionKey;
    518         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    519     } else if ([character isEqualToString:@"downArrow"]) {
    520         const unichar ch = NSDownArrowFunctionKey;
    521         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    522     } else if ([character isEqualToString:@"pageUp"]) {
    523         const unichar ch = NSPageUpFunctionKey;
    524         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    525     } else if ([character isEqualToString:@"pageDown"]) {
    526         const unichar ch = NSPageDownFunctionKey;
    527         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    528     } else if ([character isEqualToString:@"home"]) {
    529         const unichar ch = NSHomeFunctionKey;
    530         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    531     } else if ([character isEqualToString:@"end"]) {
    532         const unichar ch = NSEndFunctionKey;
    533         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    534     } else if ([character isEqualToString:@"delete"]) {
    535         const unichar ch = 0x7f;
    536         eventCharacter = [NSString stringWithCharacters:&ch length:1];
    537     }
    538 
    539     // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
    540     // If the input string is a function-key name, set its key code.
    541     for (unsigned i = 1; i <= 24; i++) {
    542         if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
    543             const unichar ch = NSF1FunctionKey + (i - 1);
    544             eventCharacter = [NSString stringWithCharacters:&ch length:1];
    545         }
    546     }
    547 
    548     NSString *charactersIgnoringModifiers = eventCharacter;
    549 
    550     int modifierFlags = 0;
    551 
    552     if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
    553         modifierFlags |= NSShiftKeyMask;
    554         charactersIgnoringModifiers = [character lowercaseString];
    555     }
    556 
    557     modifierFlags |= buildModifierFlags(modifiers);
    558 
    559     if (keyLocation == DOM_KEY_LOCATION_NUMPAD)
    560         modifierFlags |= NSNumericPadKeyMask;
    561 
    562     [[[mainFrame frameView] documentView] layout];
    563 
    564     NSEvent *event = [NSEvent keyEventWithType:NSKeyDown
    565                         location:NSMakePoint(5, 5)
    566                         modifierFlags:modifierFlags
    567                         timestamp:[self currentEventTime]
    568                         windowNumber:[[[mainFrame webView] window] windowNumber]
    569                         context:[NSGraphicsContext currentContext]
    570                         characters:eventCharacter
    571                         charactersIgnoringModifiers:charactersIgnoringModifiers
    572                         isARepeat:NO
    573                         keyCode:0];
    574 
    575     [[[[mainFrame webView] window] firstResponder] keyDown:event];
    576 
    577     event = [NSEvent keyEventWithType:NSKeyUp
    578                         location:NSMakePoint(5, 5)
    579                         modifierFlags:modifierFlags
    580                         timestamp:[self currentEventTime]
    581                         windowNumber:[[[mainFrame webView] window] windowNumber]
    582                         context:[NSGraphicsContext currentContext]
    583                         characters:eventCharacter
    584                         charactersIgnoringModifiers:charactersIgnoringModifiers
    585                         isARepeat:NO
    586                         keyCode:0];
    587 
    588     [[[[mainFrame webView] window] firstResponder] keyUp:event];
    589 }
    590 
    591 - (void)enableDOMUIEventLogging:(WebScriptObject *)node
    592 {
    593     NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator];
    594     id eventName;
    595     while ((eventName = [eventEnumerator nextObject])) {
    596         [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO];
    597     }
    598 }
    599 
    600 - (void)handleEvent:(DOMEvent *)event
    601 {
    602     DOMNode *target = [event target];
    603 
    604     printf("event type:      %s\n", [[event type] UTF8String]);
    605     printf("  target:        <%s>\n", [[[target nodeName] lowercaseString] UTF8String]);
    606 
    607     if ([event isKindOfClass:[DOMEvent class]]) {
    608         printf("  eventPhase:    %d\n", [event eventPhase]);
    609         printf("  bubbles:       %d\n", [event bubbles] ? 1 : 0);
    610         printf("  cancelable:    %d\n", [event cancelable] ? 1 : 0);
    611     }
    612 
    613     if ([event isKindOfClass:[DOMUIEvent class]]) {
    614         printf("  detail:        %d\n", [(DOMUIEvent*)event detail]);
    615 
    616         DOMAbstractView *view = [(DOMUIEvent*)event view];
    617         if (view) {
    618             printf("  view:          OK");
    619             if ([view document])
    620                 printf(" (document: OK)");
    621             printf("\n");
    622         }
    623     }
    624 
    625     if ([event isKindOfClass:[DOMKeyboardEvent class]]) {
    626         printf("  keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]);
    627         printf("  keyLocation:   %d\n", [(DOMKeyboardEvent*)event keyLocation]);
    628         printf("  modifier keys: c:%d s:%d a:%d m:%d\n",
    629                [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0,
    630                [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0,
    631                [(DOMKeyboardEvent*)event altKey] ? 1 : 0,
    632                [(DOMKeyboardEvent*)event metaKey] ? 1 : 0);
    633         printf("  keyCode:       %d\n", [(DOMKeyboardEvent*)event keyCode]);
    634         printf("  charCode:      %d\n", [(DOMKeyboardEvent*)event charCode]);
    635     }
    636 
    637     if ([event isKindOfClass:[DOMMouseEvent class]]) {
    638         printf("  button:        %d\n", [(DOMMouseEvent*)event button]);
    639         printf("  clientX:       %d\n", [(DOMMouseEvent*)event clientX]);
    640         printf("  clientY:       %d\n", [(DOMMouseEvent*)event clientY]);
    641         printf("  screenX:       %d\n", [(DOMMouseEvent*)event screenX]);
    642         printf("  screenY:       %d\n", [(DOMMouseEvent*)event screenY]);
    643         printf("  modifier keys: c:%d s:%d a:%d m:%d\n",
    644                [(DOMMouseEvent*)event ctrlKey] ? 1 : 0,
    645                [(DOMMouseEvent*)event shiftKey] ? 1 : 0,
    646                [(DOMMouseEvent*)event altKey] ? 1 : 0,
    647                [(DOMMouseEvent*)event metaKey] ? 1 : 0);
    648         id relatedTarget = [(DOMMouseEvent*)event relatedTarget];
    649         if (relatedTarget) {
    650             printf("  relatedTarget: %s", [[[relatedTarget class] description] UTF8String]);
    651             if ([relatedTarget isKindOfClass:[DOMNode class]])
    652                 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]);
    653             printf("\n");
    654         }
    655     }
    656 
    657     if ([event isKindOfClass:[DOMMutationEvent class]]) {
    658         printf("  prevValue:     %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]);
    659         printf("  newValue:      %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]);
    660         printf("  attrName:      %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]);
    661         printf("  attrChange:    %d\n", [(DOMMutationEvent*)event attrChange]);
    662         DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode];
    663         if (relatedNode) {
    664             printf("  relatedNode:   %s (nodeName: %s)\n",
    665                    [[[relatedNode class] description] UTF8String],
    666                    [[relatedNode nodeName] UTF8String]);
    667         }
    668     }
    669 
    670     if ([event isKindOfClass:[DOMWheelEvent class]]) {
    671         printf("  clientX:       %d\n", [(DOMWheelEvent*)event clientX]);
    672         printf("  clientY:       %d\n", [(DOMWheelEvent*)event clientY]);
    673         printf("  screenX:       %d\n", [(DOMWheelEvent*)event screenX]);
    674         printf("  screenY:       %d\n", [(DOMWheelEvent*)event screenY]);
    675         printf("  modifier keys: c:%d s:%d a:%d m:%d\n",
    676                [(DOMWheelEvent*)event ctrlKey] ? 1 : 0,
    677                [(DOMWheelEvent*)event shiftKey] ? 1 : 0,
    678                [(DOMWheelEvent*)event altKey] ? 1 : 0,
    679                [(DOMWheelEvent*)event metaKey] ? 1 : 0);
    680         printf("  isHorizontal:  %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0);
    681         printf("  wheelDelta:    %d\n", [(DOMWheelEvent*)event wheelDelta]);
    682     }
    683 }
    684 
    685 // FIXME: It's not good to have a test hard-wired into this controller like this.
    686 // Instead we need to get testing framework based on the Objective-C bindings
    687 // to work well enough that we can test that way instead.
    688 - (void)fireKeyboardEventsToElement:(WebScriptObject *)element {
    689 
    690     if (![element isKindOfClass:[DOMHTMLElement class]])
    691         return;
    692 
    693     DOMHTMLElement *target = (DOMHTMLElement*)element;
    694     DOMDocument *document = [target ownerDocument];
    695 
    696     // Keyboard Event 1
    697 
    698     DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"];
    699     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown"
    700                                          canBubble:YES
    701                                         cancelable:YES
    702                                               view:[document defaultView]
    703                                      keyIdentifier:@"U+000041"
    704                                        keyLocation:0
    705                                            ctrlKey:YES
    706                                             altKey:NO
    707                                           shiftKey:NO
    708                                            metaKey:NO];
    709     [target dispatchEvent:domEvent];
    710 
    711     // Keyboard Event 2
    712 
    713     domEvent = [document createEvent:@"KeyboardEvent"];
    714     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress"
    715                                          canBubble:YES
    716                                         cancelable:YES
    717                                               view:[document defaultView]
    718                                      keyIdentifier:@"U+000045"
    719                                        keyLocation:1
    720                                            ctrlKey:NO
    721                                             altKey:YES
    722                                           shiftKey:NO
    723                                            metaKey:NO];
    724     [target dispatchEvent:domEvent];
    725 
    726     // Keyboard Event 3
    727 
    728     domEvent = [document createEvent:@"KeyboardEvent"];
    729     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup"
    730                                          canBubble:YES
    731                                         cancelable:YES
    732                                               view:[document defaultView]
    733                                      keyIdentifier:@"U+000056"
    734                                        keyLocation:0
    735                                            ctrlKey:NO
    736                                             altKey:NO
    737                                           shiftKey:NO
    738                                            metaKey:NO];
    739     [target dispatchEvent:domEvent];
    740 
    741 }
    742 
    743 @end
    744