• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 Jonas Witt <jonas.witt@gmail.com>
4 * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
5 * Copyright (C) 2006 Alexey Proskuryakov <ap@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
44extern "C" void _NSNewKillRingSequence();
45
46enum MouseAction {
47    MouseDown,
48    MouseUp,
49    MouseDragged
50};
51
52// Match the DOM spec (sadly the DOM spec does not provide an enum)
53enum MouseButton {
54    LeftMouseButton = 0,
55    MiddleMouseButton = 1,
56    RightMouseButton = 2,
57    NoMouseButton = -1
58};
59
60NSPoint lastMousePosition;
61NSPoint lastClickPosition;
62int lastClickButton = NoMouseButton;
63NSArray *webkitDomEventNames;
64NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once.
65BOOL 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:)
129            || aSelector == @selector(leapForward:)
130            || aSelector == @selector(mouseDown:)
131            || aSelector == @selector(mouseMoveToX:Y:)
132            || aSelector == @selector(mouseUp:)
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:))
158        return @"keyDown";
159    if (aSelector == @selector(leapForward:))
160        return @"leapForward";
161    if (aSelector == @selector(mouseDown:))
162        return @"mouseDown";
163    if (aSelector == @selector(mouseUp:))
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
211static 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- (void)mouseDown:(int)buttonNumber
289{
290    [[[mainFrame frameView] documentView] layout];
291    [self updateClickCountForButton:buttonNumber];
292
293    NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
294    NSEvent *event = [NSEvent mouseEventWithType:eventType
295                                        location:lastMousePosition
296                                   modifierFlags:0
297                                       timestamp:[self currentEventTime]
298                                    windowNumber:[[[mainFrame webView] window] windowNumber]
299                                         context:[NSGraphicsContext currentContext]
300                                     eventNumber:++eventNumber
301                                      clickCount:clickCount
302                                        pressure:0.0];
303
304    NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
305    if (subView) {
306        [subView mouseDown:event];
307        if (buttonNumber == LeftMouseButton)
308            leftMouseButtonDown = YES;
309    }
310}
311
312- (void)textZoomIn
313{
314    [[mainFrame webView] makeTextLarger:self];
315}
316
317- (void)textZoomOut
318{
319    [[mainFrame webView] makeTextSmaller:self];
320}
321
322- (void)zoomPageIn
323{
324    [[mainFrame webView] zoomPageIn:self];
325}
326
327- (void)zoomPageOut
328{
329    [[mainFrame webView] zoomPageOut:self];
330}
331
332- (void)mouseUp:(int)buttonNumber
333{
334    if (dragMode && !replayingSavedEvents) {
335        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:)]];
336        [invocation setTarget:self];
337        [invocation setSelector:@selector(mouseUp:)];
338        [invocation setArgument:&buttonNumber atIndex:2];
339
340        [EventSendingController saveEvent:invocation];
341        [EventSendingController replaySavedEvents];
342
343        return;
344    }
345
346    [[[mainFrame frameView] documentView] layout];
347    NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
348    NSEvent *event = [NSEvent mouseEventWithType:eventType
349                                        location:lastMousePosition
350                                   modifierFlags:0
351                                       timestamp:[self currentEventTime]
352                                    windowNumber:[[[mainFrame webView] window] windowNumber]
353                                         context:[NSGraphicsContext currentContext]
354                                     eventNumber:++eventNumber
355                                      clickCount:clickCount
356                                        pressure:0.0];
357
358    NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]];
359    // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView.
360    // The right solution is just to use NSApplication's built-in event sending methods,
361    // instead of rolling our own algorithm for selecting an event target.
362    targetView = targetView ? targetView : [[mainFrame frameView] documentView];
363    assert(targetView);
364    [targetView mouseUp:event];
365    if (buttonNumber == LeftMouseButton)
366        leftMouseButtonDown = NO;
367    lastClick = [event timestamp];
368    lastClickPosition = lastMousePosition;
369    if (draggingInfo) {
370        WebView *webView = [mainFrame webView];
371
372        NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo];
373
374        if (dragOperation != NSDragOperationNone)
375            [webView performDragOperation:draggingInfo];
376        else
377            [webView draggingExited:draggingInfo];
378        // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation:
379        if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)])
380            [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation];
381        [draggingInfo release];
382        draggingInfo = nil;
383    }
384}
385
386- (void)mouseMoveToX:(int)x Y:(int)y
387{
388    if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
389        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]];
390        [invocation setTarget:self];
391        [invocation setSelector:@selector(mouseMoveToX:Y:)];
392        [invocation setArgument:&x atIndex:2];
393        [invocation setArgument:&y atIndex:3];
394
395        [EventSendingController saveEvent:invocation];
396        return;
397    }
398
399    NSView *view = [mainFrame webView];
400    lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil];
401    NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved)
402                                        location:lastMousePosition
403                                   modifierFlags:0
404                                       timestamp:[self currentEventTime]
405                                    windowNumber:[[view window] windowNumber]
406                                         context:[NSGraphicsContext currentContext]
407                                     eventNumber:++eventNumber
408                                      clickCount:(leftMouseButtonDown ? clickCount : 0)
409                                        pressure:0.0];
410
411    NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
412    if (subView) {
413        if (leftMouseButtonDown) {
414            [subView mouseDragged:event];
415            if (draggingInfo) {
416                // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo:
417                if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)])
418                    [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition];
419                [[mainFrame webView] draggingUpdated:draggingInfo];
420            }
421        } else
422            [subView mouseMoved:event];
423    }
424}
425
426- (void)contextClick
427{
428    [[[mainFrame frameView] documentView] layout];
429    [self updateClickCountForButton:RightMouseButton];
430
431    NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown
432                                        location:lastMousePosition
433                                   modifierFlags:0
434                                       timestamp:[self currentEventTime]
435                                    windowNumber:[[[mainFrame webView] window] windowNumber]
436                                         context:[NSGraphicsContext currentContext]
437                                     eventNumber:++eventNumber
438                                      clickCount:clickCount
439                                        pressure:0.0];
440
441    NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
442    if (subView)
443        [subView menuForEvent:event];
444}
445
446- (void)scheduleAsynchronousClick
447{
448    [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0];
449    [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0];
450}
451
452+ (void)saveEvent:(NSInvocation *)event
453{
454    if (!savedMouseEvents)
455        savedMouseEvents = [[NSMutableArray alloc] init];
456    [savedMouseEvents addObject:event];
457}
458
459+ (void)replaySavedEvents
460{
461    replayingSavedEvents = YES;
462    while ([savedMouseEvents count]) {
463        // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate
464        NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease];
465        [savedMouseEvents removeObjectAtIndex:0];
466        [invocation invoke];
467    }
468    replayingSavedEvents = NO;
469}
470
471+ (void)clearSavedEvents
472{
473    [savedMouseEvents release];
474    savedMouseEvents = nil;
475}
476
477- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers
478{
479    NSString *eventCharacter = character;
480    if ([character isEqualToString:@"leftArrow"]) {
481        const unichar ch = NSLeftArrowFunctionKey;
482        eventCharacter = [NSString stringWithCharacters:&ch length:1];
483    } else if ([character isEqualToString:@"rightArrow"]) {
484        const unichar ch = NSRightArrowFunctionKey;
485        eventCharacter = [NSString stringWithCharacters:&ch length:1];
486    } else if ([character isEqualToString:@"upArrow"]) {
487        const unichar ch = NSUpArrowFunctionKey;
488        eventCharacter = [NSString stringWithCharacters:&ch length:1];
489    } else if ([character isEqualToString:@"downArrow"]) {
490        const unichar ch = NSDownArrowFunctionKey;
491        eventCharacter = [NSString stringWithCharacters:&ch length:1];
492    } else if ([character isEqualToString:@"pageUp"]) {
493        const unichar ch = NSPageUpFunctionKey;
494        eventCharacter = [NSString stringWithCharacters:&ch length:1];
495    } else if ([character isEqualToString:@"pageDown"]) {
496        const unichar ch = NSPageDownFunctionKey;
497        eventCharacter = [NSString stringWithCharacters:&ch length:1];
498    } else if ([character isEqualToString:@"home"]) {
499        const unichar ch = NSHomeFunctionKey;
500        eventCharacter = [NSString stringWithCharacters:&ch length:1];
501    } else if ([character isEqualToString:@"end"]) {
502        const unichar ch = NSEndFunctionKey;
503        eventCharacter = [NSString stringWithCharacters:&ch length:1];
504    } else if ([character isEqualToString:@"delete"]) {
505        const unichar ch = 0x7f;
506        eventCharacter = [NSString stringWithCharacters:&ch length:1];
507    }
508
509    NSString *charactersIgnoringModifiers = eventCharacter;
510
511    int modifierFlags = 0;
512
513    if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
514        modifierFlags |= NSShiftKeyMask;
515        charactersIgnoringModifiers = [character lowercaseString];
516    }
517
518    if ([modifiers isKindOfClass:[WebScriptObject class]]) {
519        for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
520            NSString *modifier = (NSString *)[modifiers webScriptValueAtIndex:i];
521            if ([modifier isEqual:@"ctrlKey"])
522                modifierFlags |= NSControlKeyMask;
523            else if ([modifier isEqual:@"shiftKey"])
524                modifierFlags |= NSShiftKeyMask;
525            else if ([modifier isEqual:@"altKey"])
526                modifierFlags |= NSAlternateKeyMask;
527            else if ([modifier isEqual:@"metaKey"])
528                modifierFlags |= NSCommandKeyMask;
529        }
530    }
531
532    [[[mainFrame frameView] documentView] layout];
533
534    NSEvent *event = [NSEvent keyEventWithType:NSKeyDown
535                        location:NSMakePoint(5, 5)
536                        modifierFlags:modifierFlags
537                        timestamp:[self currentEventTime]
538                        windowNumber:[[[mainFrame webView] window] windowNumber]
539                        context:[NSGraphicsContext currentContext]
540                        characters:eventCharacter
541                        charactersIgnoringModifiers:charactersIgnoringModifiers
542                        isARepeat:NO
543                        keyCode:0];
544
545    [[[[mainFrame webView] window] firstResponder] keyDown:event];
546
547    event = [NSEvent keyEventWithType:NSKeyUp
548                        location:NSMakePoint(5, 5)
549                        modifierFlags:modifierFlags
550                        timestamp:[self currentEventTime]
551                        windowNumber:[[[mainFrame webView] window] windowNumber]
552                        context:[NSGraphicsContext currentContext]
553                        characters:eventCharacter
554                        charactersIgnoringModifiers:charactersIgnoringModifiers
555                        isARepeat:NO
556                        keyCode:0];
557
558    [[[[mainFrame webView] window] firstResponder] keyUp:event];
559}
560
561- (void)enableDOMUIEventLogging:(WebScriptObject *)node
562{
563    NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator];
564    id eventName;
565    while ((eventName = [eventEnumerator nextObject])) {
566        [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO];
567    }
568}
569
570- (void)handleEvent:(DOMEvent *)event
571{
572    DOMNode *target = [event target];
573
574    printf("event type:      %s\n", [[event type] UTF8String]);
575    printf("  target:        <%s>\n", [[[target nodeName] lowercaseString] UTF8String]);
576
577    if ([event isKindOfClass:[DOMEvent class]]) {
578        printf("  eventPhase:    %d\n", [event eventPhase]);
579        printf("  bubbles:       %d\n", [event bubbles] ? 1 : 0);
580        printf("  cancelable:    %d\n", [event cancelable] ? 1 : 0);
581    }
582
583    if ([event isKindOfClass:[DOMUIEvent class]]) {
584        printf("  detail:        %d\n", [(DOMUIEvent*)event detail]);
585
586        DOMAbstractView *view = [(DOMUIEvent*)event view];
587        if (view) {
588            printf("  view:          OK");
589            if ([view document])
590                printf(" (document: OK)");
591            printf("\n");
592        }
593    }
594
595    if ([event isKindOfClass:[DOMKeyboardEvent class]]) {
596        printf("  keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]);
597        printf("  keyLocation:   %d\n", [(DOMKeyboardEvent*)event keyLocation]);
598        printf("  modifier keys: c:%d s:%d a:%d m:%d\n",
599               [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0,
600               [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0,
601               [(DOMKeyboardEvent*)event altKey] ? 1 : 0,
602               [(DOMKeyboardEvent*)event metaKey] ? 1 : 0);
603        printf("  keyCode:       %d\n", [(DOMKeyboardEvent*)event keyCode]);
604        printf("  charCode:      %d\n", [(DOMKeyboardEvent*)event charCode]);
605    }
606
607    if ([event isKindOfClass:[DOMMouseEvent class]]) {
608        printf("  button:        %d\n", [(DOMMouseEvent*)event button]);
609        printf("  clientX:       %d\n", [(DOMMouseEvent*)event clientX]);
610        printf("  clientY:       %d\n", [(DOMMouseEvent*)event clientY]);
611        printf("  screenX:       %d\n", [(DOMMouseEvent*)event screenX]);
612        printf("  screenY:       %d\n", [(DOMMouseEvent*)event screenY]);
613        printf("  modifier keys: c:%d s:%d a:%d m:%d\n",
614               [(DOMMouseEvent*)event ctrlKey] ? 1 : 0,
615               [(DOMMouseEvent*)event shiftKey] ? 1 : 0,
616               [(DOMMouseEvent*)event altKey] ? 1 : 0,
617               [(DOMMouseEvent*)event metaKey] ? 1 : 0);
618        id relatedTarget = [(DOMMouseEvent*)event relatedTarget];
619        if (relatedTarget) {
620            printf("  relatedTarget: %s", [[[relatedTarget class] description] UTF8String]);
621            if ([relatedTarget isKindOfClass:[DOMNode class]])
622                printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]);
623            printf("\n");
624        }
625    }
626
627    if ([event isKindOfClass:[DOMMutationEvent class]]) {
628        printf("  prevValue:     %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]);
629        printf("  newValue:      %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]);
630        printf("  attrName:      %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]);
631        printf("  attrChange:    %d\n", [(DOMMutationEvent*)event attrChange]);
632        DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode];
633        if (relatedNode) {
634            printf("  relatedNode:   %s (nodeName: %s)\n",
635                   [[[relatedNode class] description] UTF8String],
636                   [[relatedNode nodeName] UTF8String]);
637        }
638    }
639
640    if ([event isKindOfClass:[DOMWheelEvent class]]) {
641        printf("  clientX:       %d\n", [(DOMWheelEvent*)event clientX]);
642        printf("  clientY:       %d\n", [(DOMWheelEvent*)event clientY]);
643        printf("  screenX:       %d\n", [(DOMWheelEvent*)event screenX]);
644        printf("  screenY:       %d\n", [(DOMWheelEvent*)event screenY]);
645        printf("  modifier keys: c:%d s:%d a:%d m:%d\n",
646               [(DOMWheelEvent*)event ctrlKey] ? 1 : 0,
647               [(DOMWheelEvent*)event shiftKey] ? 1 : 0,
648               [(DOMWheelEvent*)event altKey] ? 1 : 0,
649               [(DOMWheelEvent*)event metaKey] ? 1 : 0);
650        printf("  isHorizontal:  %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0);
651        printf("  wheelDelta:    %d\n", [(DOMWheelEvent*)event wheelDelta]);
652    }
653}
654
655// FIXME: It's not good to have a test hard-wired into this controller like this.
656// Instead we need to get testing framework based on the Objective-C bindings
657// to work well enough that we can test that way instead.
658- (void)fireKeyboardEventsToElement:(WebScriptObject *)element {
659
660    if (![element isKindOfClass:[DOMHTMLElement class]])
661        return;
662
663    DOMHTMLElement *target = (DOMHTMLElement*)element;
664    DOMDocument *document = [target ownerDocument];
665
666    // Keyboard Event 1
667
668    DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"];
669    [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown"
670                                         canBubble:YES
671                                        cancelable:YES
672                                              view:[document defaultView]
673                                     keyIdentifier:@"U+000041"
674                                       keyLocation:0
675                                           ctrlKey:YES
676                                            altKey:NO
677                                          shiftKey:NO
678                                           metaKey:NO];
679    [target dispatchEvent:domEvent];
680
681    // Keyboard Event 2
682
683    domEvent = [document createEvent:@"KeyboardEvent"];
684    [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress"
685                                         canBubble:YES
686                                        cancelable:YES
687                                              view:[document defaultView]
688                                     keyIdentifier:@"U+000045"
689                                       keyLocation:1
690                                           ctrlKey:NO
691                                            altKey:YES
692                                          shiftKey:NO
693                                           metaKey:NO];
694    [target dispatchEvent:domEvent];
695
696    // Keyboard Event 3
697
698    domEvent = [document createEvent:@"KeyboardEvent"];
699    [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup"
700                                         canBubble:YES
701                                        cancelable:YES
702                                              view:[document defaultView]
703                                     keyIdentifier:@"U+000056"
704                                       keyLocation:0
705                                           ctrlKey:NO
706                                            altKey:NO
707                                          shiftKey:NO
708                                           metaKey:NO];
709    [target dispatchEvent:domEvent];
710
711}
712
713@end
714