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