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