Unreviewed, rolling out r164073 and r164080.
[WebKit-https.git] / Tools / DumpRenderTree / mac / EventSendingController.mm
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 <WebKit/DOMPrivate.h>
40 #import <WebKit/WebKit.h>
41 #import <WebKit/WebViewPrivate.h>
42
43 #if !PLATFORM(IOS)
44 #import <Carbon/Carbon.h>                           // for GetCurrentEventTime()
45 #endif
46
47 #if PLATFORM(IOS)
48 #import <GraphicsServices/GraphicsServices.h>       // for GSCurrentEventTimestamp()
49 #import <WebKit/KeyEventCodesIOS.h>
50 #import <WebKit/WAKWindow.h>
51 #import <WebKit/WebEvent.h>
52 #import <UIKit/UIKit.h>
53 #endif
54
55 #if !PLATFORM(IOS)
56 extern "C" void _NSNewKillRingSequence();
57
58 @interface NSApplication (Details)
59 - (void)_setCurrentEvent:(NSEvent *)event;
60 @end
61 #endif
62
63 enum MouseAction {
64     MouseDown,
65     MouseUp,
66     MouseDragged
67 };
68
69 // Match the DOM spec (sadly the DOM spec does not provide an enum)
70 enum MouseButton {
71     LeftMouseButton = 0,
72     MiddleMouseButton = 1,
73     RightMouseButton = 2,
74     NoMouseButton = -1
75 };
76
77 struct KeyMappingEntry {
78     int macKeyCode;
79     int macNumpadKeyCode;
80     unichar character;
81     NSString* characterName;
82 };
83
84 NSPoint lastMousePosition;
85 NSPoint lastClickPosition;
86 int lastClickButton = NoMouseButton;
87 NSArray *webkitDomEventNames;
88 NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once.
89 BOOL replayingSavedEvents;
90
91
92 #if PLATFORM(IOS)
93 @interface SyntheticTouch : NSObject {
94 @public
95     CGPoint _location;
96     UITouchPhase _phase;
97     unsigned _identifier;
98 };
99
100 @property (nonatomic) CGPoint location;
101 @property (nonatomic) UITouchPhase phase;
102 @property (nonatomic) unsigned identifier;
103
104 + (id)touchWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier;
105 - (id)initWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier;
106 @end
107
108 @implementation SyntheticTouch
109
110 @synthesize location = _location;
111 @synthesize phase = _phase;
112 @synthesize identifier = _identifier;
113
114 + (id)touchWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier
115 {
116     return [[[SyntheticTouch alloc] initWithLocation:location phase:phase identifier:identifier] autorelease];
117 }
118
119 - (id)initWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier
120 {
121     if ((self = [super init])) {
122         _location = location;
123         _phase = phase;
124         _identifier = identifier;
125     }
126     return self;
127 }
128 @end // SyntheticTouch
129 #endif
130
131 @implementation EventSendingController
132
133 + (void)initialize
134 {
135     webkitDomEventNames = [[NSArray alloc] initWithObjects:
136         @"abort",
137         @"beforecopy",
138         @"beforecut",
139         @"beforepaste",
140         @"blur",
141         @"change",
142         @"click",
143         @"contextmenu",
144         @"copy",
145         @"cut",
146         @"dblclick",
147         @"drag",
148         @"dragend",
149         @"dragenter",
150         @"dragleave",
151         @"dragover",
152         @"dragstart",
153         @"drop",
154         @"error",
155         @"focus",
156         @"input",
157         @"keydown",
158         @"keypress",
159         @"keyup",
160         @"load",
161         @"mousedown",
162         @"mousemove",
163         @"mouseout",
164         @"mouseover",
165         @"mouseup",
166         @"mousewheel",
167         @"beforeunload",
168         @"paste",
169         @"readystatechange",
170         @"reset",
171         @"resize", 
172         @"scroll", 
173         @"search",
174         @"select",
175         @"selectstart",
176         @"submit", 
177         @"textInput", 
178         @"textzoomin",
179         @"textzoomout",
180         @"unload",
181         @"zoom",
182         nil];
183 }
184
185 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
186 {
187     if (aSelector == @selector(beginDragWithFiles:)
188             || aSelector == @selector(clearKillRing)
189             || aSelector == @selector(contextClick)
190             || aSelector == @selector(enableDOMUIEventLogging:)
191             || aSelector == @selector(fireKeyboardEventsToElement:)
192             || aSelector == @selector(keyDown:withModifiers:withLocation:)
193             || aSelector == @selector(leapForward:)
194             || aSelector == @selector(mouseDown:withModifiers:)
195             || aSelector == @selector(mouseMoveToX:Y:)
196             || aSelector == @selector(mouseUp:withModifiers:)
197             || aSelector == @selector(scheduleAsynchronousClick)
198             || aSelector == @selector(scheduleAsynchronousKeyDown:withModifiers:withLocation:)
199             || aSelector == @selector(textZoomIn)
200             || aSelector == @selector(textZoomOut)
201             || aSelector == @selector(zoomPageIn)
202             || aSelector == @selector(zoomPageOut)
203             || aSelector == @selector(scalePageBy:atX:andY:)
204             || aSelector == @selector(mouseScrollByX:andY:)
205             || aSelector == @selector(continuousMouseScrollByX:andY:)
206 #if PLATFORM(IOS)
207             || aSelector == @selector(addTouchAtX:y:)
208             || aSelector == @selector(updateTouchAtIndex:x:y:)
209             || aSelector == @selector(cancelTouchAtIndex:)
210             || aSelector == @selector(clearTouchPoints)
211             || aSelector == @selector(markAllTouchesAsStationary)
212             || aSelector == @selector(releaseTouchAtIndex:)
213             || aSelector == @selector(setTouchModifier:value:)
214             || aSelector == @selector(touchStart)
215             || aSelector == @selector(touchMove)
216             || aSelector == @selector(touchEnd)
217             || aSelector == @selector(touchCancel)
218 #endif            
219             )
220         return NO;
221     return YES;
222 }
223
224 + (BOOL)isKeyExcludedFromWebScript:(const char*)name
225 {
226     if (strcmp(name, "dragMode") == 0)
227         return NO;
228     return YES;
229 }
230
231 + (NSString *)webScriptNameForSelector:(SEL)aSelector
232 {
233     if (aSelector == @selector(beginDragWithFiles:))
234         return @"beginDragWithFiles";
235     if (aSelector == @selector(contextClick))
236         return @"contextClick";
237     if (aSelector == @selector(enableDOMUIEventLogging:))
238         return @"enableDOMUIEventLogging";
239     if (aSelector == @selector(fireKeyboardEventsToElement:))
240         return @"fireKeyboardEventsToElement";
241     if (aSelector == @selector(keyDown:withModifiers:withLocation:))
242         return @"keyDown";
243     if (aSelector == @selector(scheduleAsynchronousKeyDown:withModifiers:withLocation:))
244         return @"scheduleAsynchronousKeyDown";
245     if (aSelector == @selector(leapForward:))
246         return @"leapForward";
247     if (aSelector == @selector(mouseDown:withModifiers:))
248         return @"mouseDown";
249     if (aSelector == @selector(mouseUp:withModifiers:))
250         return @"mouseUp";
251     if (aSelector == @selector(mouseMoveToX:Y:))
252         return @"mouseMoveTo";
253     if (aSelector == @selector(setDragMode:))
254         return @"setDragMode";
255     if (aSelector == @selector(mouseScrollByX:andY:))
256         return @"mouseScrollBy";
257     if (aSelector == @selector(continuousMouseScrollByX:andY:))
258         return @"continuousMouseScrollBy";
259     if (aSelector == @selector(scalePageBy:atX:andY:))
260         return @"scalePageBy";
261 #if PLATFORM(IOS)
262     if (aSelector == @selector(addTouchAtX:y:))
263         return @"addTouchPoint";
264     if (aSelector == @selector(updateTouchAtIndex:x:y:))
265         return @"updateTouchPoint";
266     if (aSelector == @selector(cancelTouchAtIndex:))
267         return @"cancelTouchPoint";
268     if (aSelector == @selector(clearTouchPoints))
269         return @"clearTouchPoints";
270     if (aSelector == @selector(markAllTouchesAsStationary))
271         return @"markAllTouchesAsStationary";
272     if (aSelector == @selector(releaseTouchAtIndex:))
273         return @"releaseTouchPoint";
274     if (aSelector == @selector(setTouchModifier:value:))
275         return @"setTouchModifier";
276     if (aSelector == @selector(touchStart))
277         return @"touchStart";
278     if (aSelector == @selector(touchMove))
279         return @"touchMove";
280     if (aSelector == @selector(touchEnd))
281         return @"touchEnd";
282     if (aSelector == @selector(touchCancel))
283         return @"touchCancel";
284 #endif
285     return nil;
286 }
287
288 - (id)init
289 {
290     self = [super init];
291     if (self)
292         dragMode = YES;
293     return self;
294 }
295
296 - (void)dealloc
297 {
298 #if PLATFORM(IOS)
299     [touches release];
300 #endif
301     [super dealloc];
302 }
303
304 - (double)currentEventTime
305 {
306 #if !PLATFORM(IOS)
307     return GetCurrentEventTime() + timeOffset;
308 #else
309     return GSCurrentEventTimestamp() + timeOffset;
310 #endif
311 }
312
313 - (void)leapForward:(int)milliseconds
314 {
315     if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
316         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]];
317         [invocation setTarget:self];
318         [invocation setSelector:@selector(leapForward:)];
319         [invocation setArgument:&milliseconds atIndex:2];
320         
321         [EventSendingController saveEvent:invocation];
322         
323         return;
324     }
325
326     timeOffset += milliseconds / 1000.0;
327 }
328
329 - (void)clearKillRing
330 {
331 #if !PLATFORM(IOS)
332     _NSNewKillRingSequence();
333 #endif
334 }
335
336 #if !PLATFORM(IOS)
337 static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action)
338 {
339     switch (button) {
340         case LeftMouseButton:
341             switch (action) {
342                 case MouseDown:
343                     return NSLeftMouseDown;
344                 case MouseUp:
345                     return NSLeftMouseUp;
346                 case MouseDragged:
347                     return NSLeftMouseDragged;
348             }
349         case RightMouseButton:
350             switch (action) {
351                 case MouseDown:
352                     return NSRightMouseDown;
353                 case MouseUp:
354                     return NSRightMouseUp;
355                 case MouseDragged:
356                     return NSRightMouseDragged;
357             }
358         default:
359             switch (action) {
360                 case MouseDown:
361                     return NSOtherMouseDown;
362                 case MouseUp:
363                     return NSOtherMouseUp;
364                 case MouseDragged:
365                     return NSOtherMouseDragged;
366             }
367     }
368     assert(0);
369     return static_cast<NSEventType>(0);
370 }
371
372 - (void)beginDragWithFiles:(WebScriptObject*)jsFilePaths
373 {
374     assert(!draggingInfo);
375     assert([jsFilePaths isKindOfClass:[WebScriptObject class]]);
376
377     NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName];
378     [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
379
380     NSURL *currentTestURL = [NSURL URLWithString:[[mainFrame webView] mainFrameURL]];
381
382     NSMutableArray *filePaths = [NSMutableArray array];
383     for (unsigned i = 0; [[jsFilePaths webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
384         NSString *filePath = (NSString *)[jsFilePaths webScriptValueAtIndex:i];
385         // Have NSURL encode the name so that we handle '?' in file names correctly.
386         NSURL *fileURL = [NSURL fileURLWithPath:filePath];
387         NSURL *absoluteFileURL = [NSURL URLWithString:[fileURL relativeString]  relativeToURL:currentTestURL];
388         [filePaths addObject:[absoluteFileURL path]];
389     }
390
391     [pboard setPropertyList:filePaths forType:NSFilenamesPboardType];
392     assert([pboard propertyListForType:NSFilenamesPboardType]); // setPropertyList will silently fail on error, assert that it didn't fail
393
394     // Provide a source, otherwise [DumpRenderTreeDraggingInfo draggingSourceOperationMask] defaults to NSDragOperationNone
395     DumpRenderTreeFileDraggingSource *source = [[[DumpRenderTreeFileDraggingSource alloc] init] autorelease];
396     draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pboard source:source];
397     [[mainFrame webView] draggingEntered:draggingInfo];
398
399     dragMode = NO; // dragMode saves events and then replays them later.  We don't need/want that.
400     leftMouseButtonDown = YES; // Make the rest of eventSender think a drag is in progress
401 }
402 #endif // !PLATFORM(IOS)
403
404 - (void)updateClickCountForButton:(int)buttonNumber
405 {
406     if (([self currentEventTime] - lastClick >= 1) ||
407         !NSEqualPoints(lastMousePosition, lastClickPosition) ||
408         lastClickButton != buttonNumber) {
409         clickCount = 1;
410         lastClickButton = buttonNumber;
411     } else
412         clickCount++;
413 }
414
415 static int modifierFlags(const NSString* modifierName)
416 {
417 #if !PLATFORM(IOS)
418     const int controlKeyMask = NSControlKeyMask;
419     const int shiftKeyMask = NSShiftKeyMask;
420     const int alternateKeyMask = NSAlternateKeyMask;
421     const int commandKeyMask = NSCommandKeyMask;
422 #else
423     const int controlKeyMask = WebEventFlagMaskControl;
424     const int shiftKeyMask = WebEventFlagMaskShift;
425     const int alternateKeyMask = WebEventFlagMaskAlternate;
426     const int commandKeyMask = WebEventFlagMaskCommand;
427 #endif
428
429     int flags = 0;
430     if ([modifierName isEqual:@"ctrlKey"])
431         flags |= controlKeyMask;
432     else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"])
433         flags |= shiftKeyMask;
434     else if ([modifierName isEqual:@"altKey"])
435         flags |= alternateKeyMask;
436     else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"])
437         flags |= commandKeyMask;
438
439     return flags;
440 }
441
442 static int buildModifierFlags(const WebScriptObject* modifiers)
443 {
444     int flags = 0;
445     if ([modifiers isKindOfClass:[NSString class]])
446         return modifierFlags((NSString*)modifiers);
447     else if (![modifiers isKindOfClass:[WebScriptObject class]])
448         return flags;
449     for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
450         NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i];
451         flags |= modifierFlags(modifierName);
452     }
453     return flags;
454 }
455
456 - (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
457 {
458     [[[mainFrame frameView] documentView] layout];
459     [self updateClickCountForButton:buttonNumber];
460     
461 #if !PLATFORM(IOS)
462     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
463     NSEvent *event = [NSEvent mouseEventWithType:eventType
464                                         location:lastMousePosition 
465                                    modifierFlags:buildModifierFlags(modifiers)
466                                        timestamp:[self currentEventTime]
467                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
468                                          context:[NSGraphicsContext currentContext] 
469                                      eventNumber:++eventNumber 
470                                       clickCount:clickCount 
471                                         pressure:0.0];
472 #else
473     WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseDown
474                                                      timeStamp:[self currentEventTime]
475                                                       location:lastMousePosition];
476 #endif
477
478     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
479     if (subView) {
480 #if !PLATFORM(IOS)
481         [NSApp _setCurrentEvent:event];
482 #endif
483         [subView mouseDown:event];
484 #if !PLATFORM(IOS)
485         [NSApp _setCurrentEvent:nil];
486 #endif
487         if (buttonNumber == LeftMouseButton)
488             leftMouseButtonDown = YES;
489     }
490
491 #if PLATFORM(IOS)
492     [event release];
493 #endif
494 }
495
496 - (void)mouseDown:(int)buttonNumber
497 {
498     [self mouseDown:buttonNumber withModifiers:nil];
499 }
500
501 - (void)textZoomIn
502 {
503     [[mainFrame webView] makeTextLarger:self];
504 }
505
506 - (void)textZoomOut
507 {
508     [[mainFrame webView] makeTextSmaller:self];
509 }
510
511 - (void)zoomPageIn
512 {
513     [[mainFrame webView] zoomPageIn:self];
514 }
515
516 - (void)zoomPageOut
517 {
518     [[mainFrame webView] zoomPageOut:self];
519 }
520
521 - (void)scalePageBy:(float)scale atX:(float)x andY:(float)y
522 {
523 #if !PLATFORM(IOS)
524     // -[WebView _scaleWebView:] is Mac-specific API, and calls functions that
525     // assert to not be used in iOS.
526     [[mainFrame webView] _scaleWebView:scale atOrigin:NSMakePoint(x, y)];
527 #endif
528 }
529
530 - (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
531 {
532     if (dragMode && !replayingSavedEvents) {
533         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]];
534         [invocation setTarget:self];
535         [invocation setSelector:@selector(mouseUp:withModifiers:)];
536         [invocation setArgument:&buttonNumber atIndex:2];
537         [invocation setArgument:&modifiers atIndex:3];
538         
539         [EventSendingController saveEvent:invocation];
540         [EventSendingController replaySavedEvents];
541
542         return;
543     }
544
545     [[[mainFrame frameView] documentView] layout];
546 #if !PLATFORM(IOS)
547     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
548     NSEvent *event = [NSEvent mouseEventWithType:eventType
549                                         location:lastMousePosition 
550                                    modifierFlags:buildModifierFlags(modifiers)
551                                        timestamp:[self currentEventTime]
552                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
553                                          context:[NSGraphicsContext currentContext] 
554                                      eventNumber:++eventNumber 
555                                       clickCount:clickCount 
556                                         pressure:0.0];
557 #else
558     WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseUp
559                                                      timeStamp:[self currentEventTime]
560                                                       location:lastMousePosition];
561 #endif
562
563     NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]];
564     // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView.
565     // The right solution is just to use NSApplication's built-in event sending methods, 
566     // instead of rolling our own algorithm for selecting an event target.
567     targetView = targetView ? targetView : [[mainFrame frameView] documentView];
568     assert(targetView);
569 #if !PLATFORM(IOS)
570     [NSApp _setCurrentEvent:event];
571 #endif
572     [targetView mouseUp:event];
573 #if !PLATFORM(IOS)
574     [NSApp _setCurrentEvent:nil];
575 #endif
576     if (buttonNumber == LeftMouseButton)
577         leftMouseButtonDown = NO;
578     lastClick = [event timestamp];
579     lastClickPosition = lastMousePosition;
580 #if !PLATFORM(IOS)
581     if (draggingInfo) {
582         WebView *webView = [mainFrame webView];
583         
584         NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo];
585         
586         if (dragOperation != NSDragOperationNone)
587             [webView performDragOperation:draggingInfo];
588         else
589             [webView draggingExited:draggingInfo];
590         // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation:
591         if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)])
592             [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation];
593         [draggingInfo release];
594         draggingInfo = nil;
595     }
596 #endif
597
598 #if PLATFORM(IOS)
599     [event release];
600 #endif
601 }
602
603 - (void)mouseUp:(int)buttonNumber
604 {
605     [self mouseUp:buttonNumber withModifiers:nil];
606 }
607
608 - (void)mouseMoveToX:(int)x Y:(int)y
609 {
610     if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
611         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]];
612         [invocation setTarget:self];
613         [invocation setSelector:@selector(mouseMoveToX:Y:)];
614         [invocation setArgument:&x atIndex:2];
615         [invocation setArgument:&y atIndex:3];
616         
617         [EventSendingController saveEvent:invocation];
618         return;
619     }
620
621     NSView *view = [mainFrame webView];
622 #if !PLATFORM(IOS)
623     lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil];
624     NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved)
625                                         location:lastMousePosition 
626                                    modifierFlags:0 
627                                        timestamp:[self currentEventTime]
628                                     windowNumber:[[view window] windowNumber] 
629                                          context:[NSGraphicsContext currentContext] 
630                                      eventNumber:++eventNumber 
631                                       clickCount:(leftMouseButtonDown ? clickCount : 0) 
632                                         pressure:0.0];
633 #else
634     lastMousePosition = [view convertPoint:NSMakePoint(x, y) toView:nil];
635     WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseMoved
636                                                      timeStamp:[self currentEventTime]
637                                                       location:lastMousePosition];
638 #endif // !PLATFORM(IOS)
639
640     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
641     if (subView) {
642 #if !PLATFORM(IOS)
643         [NSApp _setCurrentEvent:event];
644 #endif
645         if (leftMouseButtonDown) {
646 #if !PLATFORM(IOS)
647             if (draggingInfo) {
648                 // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo:
649                 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)])
650                     [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition];
651                 [[mainFrame webView] draggingUpdated:draggingInfo];
652             } else
653                 [subView mouseDragged:event];
654 #endif
655         } else
656             [subView mouseMoved:event];
657 #if !PLATFORM(IOS)
658         [NSApp _setCurrentEvent:nil];
659 #endif
660     }
661
662 #if PLATFORM(IOS)
663     [event release];
664 #endif
665 }
666
667 - (void)mouseScrollByX:(int)x andY:(int)y continuously:(BOOL)c
668 {
669 #if !PLATFORM(IOS)
670     CGScrollEventUnit unit = c?kCGScrollEventUnitPixel:kCGScrollEventUnitLine;
671     CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, unit, 2, y, x);
672     
673     // CGEvent locations are in global display coordinates.
674     CGPoint lastGlobalMousePosition = {
675         lastMousePosition.x,
676         [[NSScreen mainScreen] frame].size.height - lastMousePosition.y
677     };
678     CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition);
679
680     NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent];
681     CFRelease(cgScrollEvent);
682
683     NSView *subView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]];
684     if (subView) {
685         [NSApp _setCurrentEvent:scrollEvent];
686         [subView scrollWheel:scrollEvent];
687         [NSApp _setCurrentEvent:nil];
688     }
689 #endif
690 }
691
692 - (void)continuousMouseScrollByX:(int)x andY:(int)y
693 {
694     [self mouseScrollByX:x andY:y continuously:YES];
695 }
696
697 - (void)mouseScrollByX:(int)x andY:(int)y
698 {
699     [self mouseScrollByX:x andY:y continuously:NO];
700 }
701
702 - (NSArray *)contextClick
703 {
704 #if !PLATFORM(IOS)
705     [[[mainFrame frameView] documentView] layout];
706     [self updateClickCountForButton:RightMouseButton];
707
708     NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown
709                                         location:lastMousePosition 
710                                    modifierFlags:0 
711                                        timestamp:[self currentEventTime]
712                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
713                                          context:[NSGraphicsContext currentContext] 
714                                      eventNumber:++eventNumber 
715                                       clickCount:clickCount 
716                                         pressure:0.0];
717
718     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
719     NSMutableArray *menuItemStrings = [NSMutableArray array];
720     
721     if (subView) {
722         [NSApp _setCurrentEvent:event];
723         NSMenu* menu = [subView menuForEvent:event];
724         [NSApp _setCurrentEvent:nil];
725
726         for (int i = 0; i < [menu numberOfItems]; ++i) {
727             NSMenuItem* menuItem = [menu itemAtIndex:i];
728             if (!strcmp("Inspect Element", [[menuItem title] UTF8String]))
729                 continue;
730
731             if ([menuItem isSeparatorItem])
732                 [menuItemStrings addObject:@"<separator>"];
733             else
734                 [menuItemStrings addObject:[menuItem title]];
735         }
736     }
737     
738     return menuItemStrings;
739 #else
740     return nil;
741 #endif
742 }
743
744 - (void)scheduleAsynchronousClick
745 {
746     [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0];
747     [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0];
748 }
749
750 + (void)saveEvent:(NSInvocation *)event
751 {
752     if (!savedMouseEvents)
753         savedMouseEvents = [[NSMutableArray alloc] init];
754     [savedMouseEvents addObject:event];
755 }
756
757 + (void)replaySavedEvents
758 {
759     replayingSavedEvents = YES;
760     while ([savedMouseEvents count]) {
761         // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate
762         NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease];
763         [savedMouseEvents removeObjectAtIndex:0];
764         [invocation invoke];
765     }
766     replayingSavedEvents = NO;
767 }
768
769 + (void)clearSavedEvents
770 {
771     [savedMouseEvents release];
772     savedMouseEvents = nil;
773 }
774
775 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
776 {
777     NSString *eventCharacter = character;
778     unsigned short keyCode = 0;
779     if ([character isEqualToString:@"leftArrow"]) {
780         const unichar ch = NSLeftArrowFunctionKey;
781         eventCharacter = [NSString stringWithCharacters:&ch length:1];
782         keyCode = 0x7B;
783     } else if ([character isEqualToString:@"rightArrow"]) {
784         const unichar ch = NSRightArrowFunctionKey;
785         eventCharacter = [NSString stringWithCharacters:&ch length:1];
786         keyCode = 0x7C;
787     } else if ([character isEqualToString:@"upArrow"]) {
788         const unichar ch = NSUpArrowFunctionKey;
789         eventCharacter = [NSString stringWithCharacters:&ch length:1];
790         keyCode = 0x7E;
791     } else if ([character isEqualToString:@"downArrow"]) {
792         const unichar ch = NSDownArrowFunctionKey;
793         eventCharacter = [NSString stringWithCharacters:&ch length:1];
794         keyCode = 0x7D;
795     } else if ([character isEqualToString:@"pageUp"]) {
796         const unichar ch = NSPageUpFunctionKey;
797         eventCharacter = [NSString stringWithCharacters:&ch length:1];
798         keyCode = 0x74;
799     } else if ([character isEqualToString:@"pageDown"]) {
800         const unichar ch = NSPageDownFunctionKey;
801         eventCharacter = [NSString stringWithCharacters:&ch length:1];
802         keyCode = 0x79;
803     } else if ([character isEqualToString:@"home"]) {
804         const unichar ch = NSHomeFunctionKey;
805         eventCharacter = [NSString stringWithCharacters:&ch length:1];
806         keyCode = 0x73;
807     } else if ([character isEqualToString:@"end"]) {
808         const unichar ch = NSEndFunctionKey;
809         eventCharacter = [NSString stringWithCharacters:&ch length:1];
810         keyCode = 0x77;
811     } else if ([character isEqualToString:@"insert"]) {
812         const unichar ch = NSInsertFunctionKey;
813         eventCharacter = [NSString stringWithCharacters:&ch length:1];
814         keyCode = 0x72;
815     } else if ([character isEqualToString:@"delete"]) {
816         const unichar ch = NSDeleteFunctionKey;
817         eventCharacter = [NSString stringWithCharacters:&ch length:1];
818         keyCode = 0x75;
819     } else if ([character isEqualToString:@"printScreen"]) {
820         const unichar ch = NSPrintScreenFunctionKey;
821         eventCharacter = [NSString stringWithCharacters:&ch length:1];
822         keyCode = 0x0; // There is no known virtual key code for PrintScreen.
823     } else if ([character isEqualToString:@"cyrillicSmallLetterA"]) {
824         const unichar ch = 0x0430;
825         eventCharacter = [NSString stringWithCharacters:&ch length:1];
826         keyCode = 0x3; // Shares key with "F" on Russian layout.
827     } else if ([character isEqualToString:@"leftControl"]) {
828         const unichar ch = 0xFFE3;
829         eventCharacter = [NSString stringWithCharacters:&ch length:1];
830         keyCode = 0x3B;
831     } else if ([character isEqualToString:@"leftShift"]) {
832         const unichar ch = 0xFFE1;
833         eventCharacter = [NSString stringWithCharacters:&ch length:1];
834         keyCode = 0x38;
835     } else if ([character isEqualToString:@"leftAlt"]) {
836         const unichar ch = 0xFFE7;
837         eventCharacter = [NSString stringWithCharacters:&ch length:1];
838         keyCode = 0x3A;
839     } else if ([character isEqualToString:@"rightControl"]) {
840         const unichar ch = 0xFFE4;
841         eventCharacter = [NSString stringWithCharacters:&ch length:1];
842         keyCode = 0x3E;
843     } else if ([character isEqualToString:@"rightShift"]) {
844         const unichar ch = 0xFFE2;
845         eventCharacter = [NSString stringWithCharacters:&ch length:1];
846         keyCode = 0x3C;
847     } else if ([character isEqualToString:@"rightAlt"]) {
848         const unichar ch = 0xFFE8;
849         eventCharacter = [NSString stringWithCharacters:&ch length:1];
850         keyCode = 0x3D;
851     }
852
853     // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
854     // If the input string is a function-key name, set its key code.
855     for (unsigned i = 1; i <= 24; i++) {
856         if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
857             const unichar ch = NSF1FunctionKey + (i - 1);
858             eventCharacter = [NSString stringWithCharacters:&ch length:1];
859             switch (i) {
860                 case 1: keyCode = 0x7A; break;
861                 case 2: keyCode = 0x78; break;
862                 case 3: keyCode = 0x63; break;
863                 case 4: keyCode = 0x76; break;
864                 case 5: keyCode = 0x60; break;
865                 case 6: keyCode = 0x61; break;
866                 case 7: keyCode = 0x62; break;
867                 case 8: keyCode = 0x64; break;
868                 case 9: keyCode = 0x65; break;
869                 case 10: keyCode = 0x6D; break;
870                 case 11: keyCode = 0x67; break;
871                 case 12: keyCode = 0x6F; break;
872                 case 13: keyCode = 0x69; break;
873                 case 14: keyCode = 0x6B; break;
874                 case 15: keyCode = 0x71; break;
875                 case 16: keyCode = 0x6A; break;
876                 case 17: keyCode = 0x40; break;
877                 case 18: keyCode = 0x4F; break;
878                 case 19: keyCode = 0x50; break;
879                 case 20: keyCode = 0x5A; break;
880             }
881         }
882     }
883
884     // FIXME: No keyCode is set for most keys.
885     if ([character isEqualToString:@"\t"])
886         keyCode = 0x30;
887     else if ([character isEqualToString:@" "])
888         keyCode = 0x31;
889     else if ([character isEqualToString:@"\r"])
890         keyCode = 0x24;
891     else if ([character isEqualToString:@"\n"])
892         keyCode = 0x4C;
893     else if ([character isEqualToString:@"\x8"])
894         keyCode = 0x33;
895     else if ([character isEqualToString:@"a"])
896         keyCode = 0x00;
897     else if ([character isEqualToString:@"b"])
898         keyCode = 0x0B;
899     else if ([character isEqualToString:@"d"])
900         keyCode = 0x02;
901     else if ([character isEqualToString:@"e"])
902         keyCode = 0x0E;
903
904     KeyMappingEntry table[] = {
905         {0x2F, 0x41, '.', nil},
906         {0,    0x43, '*', nil},
907         {0,    0x45, '+', nil},
908         {0,    0x47, NSClearLineFunctionKey, @"clear"},
909         {0x2C, 0x4B, '/', nil},
910         {0,    0x4C, 3, @"enter" },
911         {0x1B, 0x4E, '-', nil},
912         {0x18, 0x51, '=', nil},
913         {0x1D, 0x52, '0', nil},
914         {0x12, 0x53, '1', nil},
915         {0x13, 0x54, '2', nil},
916         {0x14, 0x55, '3', nil},
917         {0x15, 0x56, '4', nil},
918         {0x17, 0x57, '5', nil},
919         {0x16, 0x58, '6', nil},
920         {0x1A, 0x59, '7', nil},
921         {0x1C, 0x5B, '8', nil},
922         {0x19, 0x5C, '9', nil},
923     };
924     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) {
925         NSString* currentCharacterString = [NSString stringWithCharacters:&table[i].character length:1];
926         if ([character isEqualToString:currentCharacterString] || [character isEqualToString:table[i].characterName]) {
927             if (location == DOM_KEY_LOCATION_NUMPAD)
928                 keyCode = table[i].macNumpadKeyCode;
929             else
930                 keyCode = table[i].macKeyCode;
931             eventCharacter = currentCharacterString;
932             break;
933         }
934     }
935
936     NSString *charactersIgnoringModifiers = eventCharacter;
937
938     int modifierFlags = 0;
939
940     if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
941 #if !PLATFORM(IOS)
942         modifierFlags |= NSShiftKeyMask;
943 #else
944         modifierFlags |= WebEventFlagMaskAlphaShift;
945 #endif
946         charactersIgnoringModifiers = [character lowercaseString];
947     }
948
949     modifierFlags |= buildModifierFlags(modifiers);
950
951     if (location == DOM_KEY_LOCATION_NUMPAD)
952         modifierFlags |= NSNumericPadKeyMask;
953
954     [[[mainFrame frameView] documentView] layout];
955
956 #if !PLATFORM(IOS)
957     NSEvent *event = [NSEvent keyEventWithType:NSKeyDown
958                         location:NSMakePoint(5, 5)
959                         modifierFlags:modifierFlags
960                         timestamp:[self currentEventTime]
961                         windowNumber:[[[mainFrame webView] window] windowNumber]
962                         context:[NSGraphicsContext currentContext]
963                         characters:eventCharacter
964                         charactersIgnoringModifiers:charactersIgnoringModifiers
965                         isARepeat:NO
966                         keyCode:keyCode];
967 #else
968     WebEvent *event = [[WebEvent alloc] initWithKeyEventType:WebEventKeyDown
969                         timeStamp:[self currentEventTime]
970                         characters:eventCharacter
971                         charactersIgnoringModifiers:charactersIgnoringModifiers
972                         modifiers:(WebEventFlags)modifierFlags
973                         isRepeating:NO
974                         isPopupVariant:NO
975                         keyCode:[character characterAtIndex:0]
976                         isTabKey:([character characterAtIndex:0] == '\t')
977                         characterSet:WebEventCharacterSetASCII];
978 #endif
979
980 #if !PLATFORM(IOS)
981     [NSApp _setCurrentEvent:event];
982 #endif
983     [[[[mainFrame webView] window] firstResponder] keyDown:event];
984 #if !PLATFORM(IOS)
985     [NSApp _setCurrentEvent:nil];
986 #endif
987
988 #if !PLATFORM(IOS)
989     event = [NSEvent keyEventWithType:NSKeyUp
990                         location:NSMakePoint(5, 5)
991                         modifierFlags:modifierFlags
992                         timestamp:[self currentEventTime]
993                         windowNumber:[[[mainFrame webView] window] windowNumber]
994                         context:[NSGraphicsContext currentContext]
995                         characters:eventCharacter
996                         charactersIgnoringModifiers:charactersIgnoringModifiers
997                         isARepeat:NO
998                         keyCode:keyCode];
999 #else
1000     [event release];
1001     event = [[WebEvent alloc] initWithKeyEventType:WebEventKeyUp
1002                         timeStamp:[self currentEventTime]
1003                         characters:eventCharacter
1004                         charactersIgnoringModifiers:charactersIgnoringModifiers
1005                         modifiers:(WebEventFlags)modifierFlags
1006                         isRepeating:NO
1007                         isPopupVariant:NO
1008                         keyCode:[character characterAtIndex:0]
1009                         isTabKey:([character characterAtIndex:0] == '\t')
1010                         characterSet:WebEventCharacterSetASCII];
1011 #endif
1012
1013 #if !PLATFORM(IOS)
1014     [NSApp _setCurrentEvent:event];
1015 #endif
1016     [[[[mainFrame webView] window] firstResponder] keyUp:event];
1017 #if !PLATFORM(IOS)
1018     [NSApp _setCurrentEvent:nil];
1019 #endif
1020
1021 #if PLATFORM(IOS)
1022     [event release];
1023 #endif
1024 }
1025
1026 - (void)keyDownWrapper:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
1027 {
1028     [self keyDown:character withModifiers:modifiers withLocation:location];
1029 }
1030
1031 - (void)scheduleAsynchronousKeyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
1032 {
1033     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(keyDownWrapper:withModifiers:withLocation:)]];
1034     [invocation retainArguments];
1035     [invocation setTarget:self];
1036     [invocation setSelector:@selector(keyDownWrapper:withModifiers:withLocation:)];
1037     [invocation setArgument:&character atIndex:2];
1038     [invocation setArgument:&modifiers atIndex:3];
1039     [invocation setArgument:&location atIndex:4];
1040     [invocation performSelector:@selector(invoke) withObject:nil afterDelay:0];
1041 }
1042
1043 - (void)enableDOMUIEventLogging:(WebScriptObject *)node
1044 {
1045     NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator];
1046     id eventName;
1047     while ((eventName = [eventEnumerator nextObject])) {
1048         [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO];
1049     }
1050 }
1051
1052 - (void)handleEvent:(DOMEvent *)event
1053 {
1054     DOMNode *target = [event target];
1055
1056     printf("event type:      %s\n", [[event type] UTF8String]);
1057     printf("  target:        <%s>\n", [[[target nodeName] lowercaseString] UTF8String]);
1058     
1059     if ([event isKindOfClass:[DOMEvent class]]) {
1060         printf("  eventPhase:    %d\n", [event eventPhase]);
1061         printf("  bubbles:       %d\n", [event bubbles] ? 1 : 0);
1062         printf("  cancelable:    %d\n", [event cancelable] ? 1 : 0);
1063     }
1064     
1065     if ([event isKindOfClass:[DOMUIEvent class]]) {
1066         printf("  detail:        %d\n", [(DOMUIEvent*)event detail]);
1067         
1068         DOMAbstractView *view = [(DOMUIEvent*)event view];
1069         if (view) {
1070             printf("  view:          OK");            
1071             if ([view document])
1072                 printf(" (document: OK)");
1073             printf("\n");
1074         }
1075     }
1076     
1077     if ([event isKindOfClass:[DOMKeyboardEvent class]]) {
1078         printf("  keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]);
1079         printf("  keyLocation:   %d\n", [(DOMKeyboardEvent*)event location]);
1080         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
1081                [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, 
1082                [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, 
1083                [(DOMKeyboardEvent*)event altKey] ? 1 : 0, 
1084                [(DOMKeyboardEvent*)event metaKey] ? 1 : 0);
1085         printf("  keyCode:       %d\n", [(DOMKeyboardEvent*)event keyCode]);
1086         printf("  charCode:      %d\n", [(DOMKeyboardEvent*)event charCode]);
1087     }
1088     
1089     if ([event isKindOfClass:[DOMMouseEvent class]]) {
1090         printf("  button:        %d\n", [(DOMMouseEvent*)event button]);
1091         printf("  clientX:       %d\n", [(DOMMouseEvent*)event clientX]);
1092         printf("  clientY:       %d\n", [(DOMMouseEvent*)event clientY]);
1093         printf("  screenX:       %d\n", [(DOMMouseEvent*)event screenX]);
1094         printf("  screenY:       %d\n", [(DOMMouseEvent*)event screenY]);
1095         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
1096                [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, 
1097                [(DOMMouseEvent*)event shiftKey] ? 1 : 0, 
1098                [(DOMMouseEvent*)event altKey] ? 1 : 0, 
1099                [(DOMMouseEvent*)event metaKey] ? 1 : 0);
1100         id relatedTarget = [(DOMMouseEvent*)event relatedTarget];
1101         if (relatedTarget) {
1102             printf("  relatedTarget: %s", [[[relatedTarget class] description] UTF8String]);
1103             if ([relatedTarget isKindOfClass:[DOMNode class]])
1104                 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]);
1105             printf("\n");
1106         }
1107     }
1108     
1109     if ([event isKindOfClass:[DOMMutationEvent class]]) {
1110         printf("  prevValue:     %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]);
1111         printf("  newValue:      %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]);
1112         printf("  attrName:      %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]);
1113         printf("  attrChange:    %d\n", [(DOMMutationEvent*)event attrChange]);
1114         DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode];
1115         if (relatedNode) {
1116             printf("  relatedNode:   %s (nodeName: %s)\n", 
1117                    [[[relatedNode class] description] UTF8String],
1118                    [[relatedNode nodeName] UTF8String]);
1119         }
1120     }
1121     
1122     if ([event isKindOfClass:[DOMWheelEvent class]]) {
1123         printf("  clientX:       %d\n", [(DOMWheelEvent*)event clientX]);
1124         printf("  clientY:       %d\n", [(DOMWheelEvent*)event clientY]);
1125         printf("  screenX:       %d\n", [(DOMWheelEvent*)event screenX]);
1126         printf("  screenY:       %d\n", [(DOMWheelEvent*)event screenY]);
1127         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
1128                [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, 
1129                [(DOMWheelEvent*)event shiftKey] ? 1 : 0, 
1130                [(DOMWheelEvent*)event altKey] ? 1 : 0, 
1131                [(DOMWheelEvent*)event metaKey] ? 1 : 0);
1132         printf("  isHorizontal:  %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0);
1133         printf("  wheelDelta:    %d\n", [(DOMWheelEvent*)event wheelDelta]);
1134     }
1135 }
1136
1137 // FIXME: It's not good to have a test hard-wired into this controller like this.
1138 // Instead we need to get testing framework based on the Objective-C bindings
1139 // to work well enough that we can test that way instead.
1140 - (void)fireKeyboardEventsToElement:(WebScriptObject *)element {
1141     
1142     if (![element isKindOfClass:[DOMHTMLElement class]])
1143         return;
1144     
1145     DOMHTMLElement *target = (DOMHTMLElement*)element;
1146     DOMDocument *document = [target ownerDocument];
1147     
1148     // Keyboard Event 1
1149     
1150     DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"];
1151     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" 
1152                                          canBubble:YES
1153                                         cancelable:YES
1154                                               view:[document defaultView]
1155                                      keyIdentifier:@"U+000041" 
1156                                        location:0
1157                                            ctrlKey:YES
1158                                             altKey:NO
1159                                           shiftKey:NO
1160                                            metaKey:NO];
1161     [target dispatchEvent:domEvent];  
1162         
1163     // Keyboard Event 2
1164     
1165     domEvent = [document createEvent:@"KeyboardEvent"];
1166     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" 
1167                                          canBubble:YES
1168                                         cancelable:YES
1169                                               view:[document defaultView]
1170                                      keyIdentifier:@"U+000045" 
1171                                        location:1
1172                                            ctrlKey:NO
1173                                             altKey:YES
1174                                           shiftKey:NO
1175                                            metaKey:NO];
1176     [target dispatchEvent:domEvent];    
1177     
1178     // Keyboard Event 3
1179     
1180     domEvent = [document createEvent:@"KeyboardEvent"];
1181     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" 
1182                                          canBubble:YES
1183                                         cancelable:YES
1184                                               view:[document defaultView]
1185                                      keyIdentifier:@"U+000056" 
1186                                        location:0
1187                                            ctrlKey:NO
1188                                             altKey:NO
1189                                           shiftKey:NO
1190                                            metaKey:NO];
1191     [target dispatchEvent:domEvent];   
1192     
1193 }
1194
1195 #if PLATFORM(IOS)
1196 - (void)addTouchAtX:(int)x y:(int)y
1197 {
1198     if (!touches)
1199         touches = [[NSMutableArray alloc] init];
1200
1201     [touches addObject:[SyntheticTouch touchWithLocation:CGPointMake(x, y) phase:UITouchPhaseBegan identifier:currentTouchIdentifier++]];
1202 }
1203
1204 - (void)cancelTouchAtIndex:(unsigned)index
1205 {
1206     if (index < [touches count])
1207         [[touches objectAtIndex:index] setPhase:UITouchPhaseCancelled];
1208 }
1209
1210 - (void)clearTouchPoints
1211 {
1212     [touches removeAllObjects];
1213 }
1214
1215 - (void)releaseTouchAtIndex:(unsigned)index
1216 {
1217     if (index < [touches count]) {
1218         SyntheticTouch *touch = [touches objectAtIndex:index];
1219         [touch setPhase:UITouchPhaseEnded];
1220     }
1221 }
1222
1223 - (void)markAllTouchesAsStationary
1224 {
1225     for (SyntheticTouch *touch in touches)
1226         [touch setPhase:UITouchPhaseStationary];
1227 }
1228
1229 - (void)updateTouchAtIndex:(unsigned)index x:(int)x y:(int)y
1230 {
1231     if (index < [touches count]) {
1232         SyntheticTouch *touch = [touches objectAtIndex:index];
1233         [touch setPhase:UITouchPhaseMoved];
1234         [touch setLocation:CGPointMake(x, y)];
1235     }
1236 }
1237
1238 - (void)setTouchModifier:(NSString*)modifierName value:(BOOL)flag
1239 {
1240     unsigned modifier = 0;
1241     
1242     if ([modifierName isEqualToString:@"alt"])
1243         modifier = WebEventFlagMaskAlternate;
1244     else if ([modifierName isEqualToString:@"shift"])
1245         modifier = WebEventFlagMaskShift;
1246     else if ([modifierName isEqualToString:@"meta"])
1247         modifier = WebEventFlagMaskCommand;
1248     else if ([modifierName isEqualToString:@"ctrl"])
1249         modifier = WebEventFlagMaskControl;
1250
1251     if (!modifier)
1252         return;
1253
1254     if (flag)
1255         nextEventFlags |= modifier;
1256     else
1257         nextEventFlags &= ~modifier;
1258 }
1259
1260 - (void)sentTouchEventOfType:(WebEventType)type
1261 {
1262     NSMutableArray *touchLocations = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1263     NSMutableArray *touchIdentifiers = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1264     NSMutableArray *touchPhases = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1265     
1266     CGPoint centroid = CGPointZero;
1267     NSUInteger touchesDownCount = 0;
1268
1269     for (SyntheticTouch *currTouch in touches) {
1270         [touchLocations addObject:[NSValue valueWithCGPoint:currTouch.location]];
1271         [touchIdentifiers addObject:[NSNumber numberWithUnsignedInt:currTouch.identifier]];
1272         [touchPhases addObject:[NSNumber numberWithUnsignedInt:currTouch.phase]];
1273
1274         if ((currTouch.phase == UITouchPhaseEnded) || (currTouch.phase == UITouchPhaseCancelled))
1275             continue;
1276         
1277         centroid.x += currTouch.location.x;
1278         centroid.y += currTouch.location.y;
1279
1280         touchesDownCount++;
1281     }
1282
1283     if (touchesDownCount > 0)
1284         centroid = CGPointMake(centroid.x / touchesDownCount, centroid.y / touchesDownCount);
1285     else
1286         centroid = CGPointZero;
1287
1288     WebEvent *event = [[WebEvent alloc] initWithTouchEventType:type
1289                                         timeStamp:[self currentEventTime]
1290                                         location:centroid
1291                                         modifiers:(WebEventFlags)nextEventFlags
1292                                         touchCount:[touches count]
1293                                         touchLocations:touchLocations
1294                                         touchIdentifiers:touchIdentifiers
1295                                         touchPhases:touchPhases
1296                                         isGesture:(touchesDownCount > 1)
1297                                         gestureScale:1
1298                                         gestureRotation:0];
1299     // Ensure that layout is up-to-date so that hit-testing through WAKViews works correctly.
1300     [mainFrame updateLayout];
1301     [[[mainFrame webView] window] sendEventSynchronously:event];
1302     [event release];
1303     
1304     [touchLocations release];
1305     [touchIdentifiers release];
1306     [touchPhases release];
1307     
1308     nextEventFlags = 0;
1309 }
1310
1311 - (void)touchStart
1312 {
1313     [self sentTouchEventOfType:WebEventTouchBegin];
1314 }
1315
1316 - (void)touchMove
1317 {
1318     [self sentTouchEventOfType:WebEventTouchChange];
1319 }
1320
1321 - (void)touchEnd
1322 {
1323     [self sentTouchEventOfType:WebEventTouchEnd];
1324
1325     NSMutableArray *touchesToRemove = [[NSMutableArray alloc] init];
1326     for (SyntheticTouch *currTouch in touches) {
1327         if (currTouch.phase == UITouchPhaseEnded)
1328             [touchesToRemove addObject:currTouch];
1329     }
1330
1331     [touches removeObjectsInArray:touchesToRemove];
1332     [touchesToRemove release];
1333 }
1334
1335 - (void)touchCancel
1336 {
1337     [self sentTouchEventOfType:WebEventTouchCancel];
1338
1339     NSMutableArray *touchesToRemove = [[NSMutableArray alloc] init];
1340     for (SyntheticTouch *currTouch in touches) {
1341         if (currTouch.phase == UITouchPhaseCancelled)
1342             [touchesToRemove addObject:currTouch];
1343     }
1344
1345     [touches removeObjectsInArray:touchesToRemove];
1346     [touchesToRemove release];
1347 }
1348 #endif
1349
1350 @end