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: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
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
288static 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