[Mac] Teach WebCore::Pasteboard about file promise drags
[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)
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)
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)
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)
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)
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)
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)
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)
363     [touches release];
364 #endif
365     [super dealloc];
366 }
367
368 - (double)currentEventTime
369 {
370 #if !PLATFORM(IOS)
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)
396     _NSNewKillRingSequence();
397 #endif
398 }
399
400 #if !PLATFORM(IOS)
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] 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     auto source = adoptNS([[DumpRenderTreeFileDraggingSource alloc] initWithPromisedFileURLs:fileURLs]);
497     draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pasteboard source:source.get()];
498     [mainFrame.webView draggingEntered:draggingInfo];
499
500     dragMode = NO;
501     leftMouseButtonDown = YES;
502 }
503 #endif // !PLATFORM(IOS)
504
505 - (void)updateClickCountForButton:(int)buttonNumber
506 {
507     if (([self currentEventTime] - lastClick >= 1) ||
508         !NSEqualPoints(lastMousePosition, lastClickPosition) ||
509         lastClickButton != buttonNumber) {
510         clickCount = 1;
511         lastClickButton = buttonNumber;
512     } else
513         clickCount++;
514 }
515
516 static int modifierFlags(const NSString* modifierName)
517 {
518 #if !PLATFORM(IOS)
519     const int controlKeyMask = NSEventModifierFlagControl;
520     const int shiftKeyMask = NSEventModifierFlagShift;
521     const int alternateKeyMask = NSEventModifierFlagOption;
522     const int commandKeyMask = NSEventModifierFlagCommand;
523     const int capsLockKeyMask = NSEventModifierFlagCapsLock;
524 #else
525     const int controlKeyMask = WebEventFlagMaskControl;
526     const int shiftKeyMask = WebEventFlagMaskShift;
527     const int alternateKeyMask = WebEventFlagMaskAlternate;
528     const int commandKeyMask = WebEventFlagMaskCommand;
529     const int capsLockKeyMask = WebEventFlagMaskAlphaShift;
530 #endif
531
532     int flags = 0;
533     if ([modifierName isEqual:@"ctrlKey"])
534         flags |= controlKeyMask;
535     else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"])
536         flags |= shiftKeyMask;
537     else if ([modifierName isEqual:@"altKey"])
538         flags |= alternateKeyMask;
539     else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"])
540         flags |= commandKeyMask;
541     else if ([modifierName isEqual:@"capsLockKey"])
542         flags |= capsLockKeyMask;
543
544     return flags;
545 }
546
547 static int buildModifierFlags(const WebScriptObject* modifiers)
548 {
549     int flags = 0;
550     if ([modifiers isKindOfClass:[NSString class]])
551         return modifierFlags((NSString*)modifiers);
552     else if (![modifiers isKindOfClass:[WebScriptObject class]])
553         return flags;
554     for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
555         NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i];
556         flags |= modifierFlags(modifierName);
557     }
558     return flags;
559 }
560
561 - (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
562 {
563     [[[mainFrame frameView] documentView] layout];
564     [self updateClickCountForButton:buttonNumber];
565     
566 #if !PLATFORM(IOS)
567     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
568     NSEvent *event = [NSEvent mouseEventWithType:eventType
569                                         location:lastMousePosition 
570                                    modifierFlags:buildModifierFlags(modifiers)
571                                        timestamp:[self currentEventTime]
572                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
573                                          context:[NSGraphicsContext currentContext] 
574                                      eventNumber:++eventNumber 
575                                       clickCount:clickCount 
576                                         pressure:0.0];
577 #else
578     WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseDown
579                                                      timeStamp:[self currentEventTime]
580                                                       location:lastMousePosition];
581 #endif
582
583     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
584     if (subView) {
585 #if !PLATFORM(IOS)
586         [NSApp _setCurrentEvent:event];
587 #endif
588         [subView mouseDown:event];
589 #if !PLATFORM(IOS)
590         [NSApp _setCurrentEvent:nil];
591 #endif
592         if (buttonNumber == LeftMouseButton)
593             leftMouseButtonDown = YES;
594     }
595
596 #if PLATFORM(IOS)
597     [event release];
598 #endif
599 }
600
601 - (void)mouseDown:(int)buttonNumber
602 {
603     [self mouseDown:buttonNumber withModifiers:nil];
604 }
605
606 - (void)textZoomIn
607 {
608     [[mainFrame webView] makeTextLarger:self];
609 }
610
611 - (void)textZoomOut
612 {
613     [[mainFrame webView] makeTextSmaller:self];
614 }
615
616 - (void)zoomPageIn
617 {
618     [[mainFrame webView] zoomPageIn:self];
619 }
620
621 - (void)zoomPageOut
622 {
623     [[mainFrame webView] zoomPageOut:self];
624 }
625
626 - (void)scalePageBy:(float)scale atX:(float)x andY:(float)y
627 {
628 #if !PLATFORM(IOS)
629     // -[WebView _scaleWebView:] is Mac-specific API, and calls functions that
630     // assert to not be used in iOS.
631     [[mainFrame webView] _scaleWebView:scale atOrigin:NSMakePoint(x, y)];
632 #endif
633 }
634
635 - (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
636 {
637     if (dragMode && !replayingSavedEvents) {
638         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]];
639         [invocation setTarget:self];
640         [invocation setSelector:@selector(mouseUp:withModifiers:)];
641         [invocation setArgument:&buttonNumber atIndex:2];
642         [invocation setArgument:&modifiers atIndex:3];
643         
644         [EventSendingController saveEvent:invocation];
645         [EventSendingController replaySavedEvents];
646
647         return;
648     }
649
650     [[[mainFrame frameView] documentView] layout];
651 #if !PLATFORM(IOS)
652     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
653     NSEvent *event = [NSEvent mouseEventWithType:eventType
654                                         location:lastMousePosition 
655                                    modifierFlags:buildModifierFlags(modifiers)
656                                        timestamp:[self currentEventTime]
657                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
658                                          context:[NSGraphicsContext currentContext] 
659                                      eventNumber:++eventNumber 
660                                       clickCount:clickCount 
661                                         pressure:0.0];
662 #else
663     WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseUp
664                                                      timeStamp:[self currentEventTime]
665                                                       location:lastMousePosition];
666 #endif
667
668     NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]];
669     // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView.
670     // The right solution is just to use NSApplication's built-in event sending methods, 
671     // instead of rolling our own algorithm for selecting an event target.
672     targetView = targetView ? targetView : [[mainFrame frameView] documentView];
673     assert(targetView);
674 #if !PLATFORM(IOS)
675     [NSApp _setCurrentEvent:event];
676 #endif
677     [targetView mouseUp:event];
678 #if !PLATFORM(IOS)
679     [NSApp _setCurrentEvent:nil];
680 #endif
681     if (buttonNumber == LeftMouseButton)
682         leftMouseButtonDown = NO;
683     lastClick = [event timestamp];
684     lastClickPosition = lastMousePosition;
685 #if !PLATFORM(IOS)
686     if (draggingInfo) {
687         WebView *webView = [mainFrame webView];
688         
689         NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo];
690         
691         if (dragOperation != NSDragOperationNone)
692             [webView performDragOperation:draggingInfo];
693         else
694             [webView draggingExited:draggingInfo];
695         // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation:
696         if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)])
697             [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation];
698         [draggingInfo release];
699         draggingInfo = nil;
700     }
701 #endif
702
703 #if PLATFORM(IOS)
704     [event release];
705 #endif
706 }
707
708 - (void)mouseUp:(int)buttonNumber
709 {
710     [self mouseUp:buttonNumber withModifiers:nil];
711 }
712
713 - (void)mouseMoveToX:(int)x Y:(int)y
714 {
715     if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
716         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]];
717         [invocation setTarget:self];
718         [invocation setSelector:@selector(mouseMoveToX:Y:)];
719         [invocation setArgument:&x atIndex:2];
720         [invocation setArgument:&y atIndex:3];
721         
722         [EventSendingController saveEvent:invocation];
723         return;
724     }
725
726     NSView *view = [mainFrame webView];
727 #if !PLATFORM(IOS)
728     NSPoint newMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil];
729     NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSEventTypeLeftMouseDragged : NSEventTypeMouseMoved)
730                                         location:newMousePosition
731                                    modifierFlags:0 
732                                        timestamp:[self currentEventTime]
733                                     windowNumber:[[view window] windowNumber] 
734                                          context:[NSGraphicsContext currentContext] 
735                                      eventNumber:++eventNumber 
736                                       clickCount:(leftMouseButtonDown ? clickCount : 0) 
737                                         pressure:0.0];
738     CGEventRef cgEvent = event.CGEvent;
739     CGEventSetIntegerValueField(cgEvent, kCGMouseEventDeltaX, newMousePosition.x - lastMousePosition.x);
740     CGEventSetIntegerValueField(cgEvent, kCGMouseEventDeltaY, newMousePosition.y - lastMousePosition.y);
741     event = [NSEvent eventWithCGEvent:cgEvent];
742     lastMousePosition = newMousePosition;
743 #else
744     lastMousePosition = [view convertPoint:NSMakePoint(x, y) toView:nil];
745     WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseMoved
746                                                      timeStamp:[self currentEventTime]
747                                                       location:lastMousePosition];
748 #endif // !PLATFORM(IOS)
749
750     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
751     if (subView) {
752 #if !PLATFORM(IOS)
753         [NSApp _setCurrentEvent:event];
754 #endif
755         if (leftMouseButtonDown) {
756 #if !PLATFORM(IOS)
757             if (draggingInfo) {
758                 // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo:
759                 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)])
760                     [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition];
761                 [[mainFrame webView] draggingUpdated:draggingInfo];
762             } else
763                 [subView mouseDragged:event];
764 #endif
765         } else
766             [subView mouseMoved:event];
767 #if !PLATFORM(IOS)
768         [NSApp _setCurrentEvent:nil];
769 #endif
770     }
771
772 #if PLATFORM(IOS)
773     [event release];
774 #endif
775 }
776
777 - (void)mouseScrollByX:(int)x andY:(int)y continuously:(BOOL)continuously
778 {
779 #if !PLATFORM(IOS)
780     CGScrollEventUnit unit = continuously ? kCGScrollEventUnitPixel : kCGScrollEventUnitLine;
781     CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, unit, 2, y, x);
782     
783     // Set the CGEvent location in flipped coords relative to the first screen, which
784     // compensates for the behavior of +[NSEvent eventWithCGEvent:] when the event has
785     // no associated window. See <rdar://problem/17180591>.
786     CGPoint lastGlobalMousePosition = CGPointMake(lastMousePosition.x, [[[NSScreen screens] objectAtIndex:0] frame].size.height - lastMousePosition.y);
787     CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition);
788
789     NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent];
790     CFRelease(cgScrollEvent);
791
792     NSView *subView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]];
793     if (subView) {
794         [NSApp _setCurrentEvent:scrollEvent];
795         [subView scrollWheel:scrollEvent];
796         [NSApp _setCurrentEvent:nil];
797     } else
798         printf("mouseScrollByXandYContinuously: Unable to locate target view for current mouse location.");
799 #endif
800 }
801
802 - (void)continuousMouseScrollByX:(int)x andY:(int)y
803 {
804     [self mouseScrollByX:x andY:y continuously:YES];
805 }
806
807 - (void)mouseScrollByX:(int)x andY:(int)y
808 {
809     [self mouseScrollByX:x andY:y continuously:NO];
810 }
811
812 - (void)mouseScrollByX:(int)x andY:(int)y withWheel:(NSString*)phaseName andMomentumPhases:(NSString*)momentumName
813 {
814 #if PLATFORM(MAC)
815     uint32_t phase = 0;
816     if ([phaseName isEqualToString: @"none"])
817         phase = 0;
818     else if ([phaseName isEqualToString: @"began"])
819         phase = 1; // kCGScrollPhaseBegan
820     else if ([phaseName isEqualToString: @"changed"])
821         phase = 2; // kCGScrollPhaseChanged;
822     else if ([phaseName isEqualToString: @"ended"])
823         phase = 4; // kCGScrollPhaseEnded
824     else if ([phaseName isEqualToString: @"cancelled"])
825         phase = 8; // kCGScrollPhaseCancelled
826     else if ([phaseName isEqualToString: @"maybegin"])
827         phase = 128; // kCGScrollPhaseMayBegin
828
829     uint32_t momentum = 0;
830     if ([momentumName isEqualToString: @"none"])
831         momentum = 0; //kCGMomentumScrollPhaseNone;
832     else if ([momentumName isEqualToString:@"begin"])
833         momentum = 1; // kCGMomentumScrollPhaseBegin;
834     else if ([momentumName isEqualToString:@"continue"])
835         momentum = 2; // kCGMomentumScrollPhaseContinue;
836     else if ([momentumName isEqualToString:@"end"])
837         momentum = 3; // kCGMomentumScrollPhaseEnd;
838
839     CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 2, y, x);
840
841     // Set the CGEvent location in flipped coords relative to the first screen, which
842     // compensates for the behavior of +[NSEvent eventWithCGEvent:] when the event has
843     // no associated window. See <rdar://problem/17180591>.
844     CGPoint lastGlobalMousePosition = CGPointMake(lastMousePosition.x, [[[NSScreen screens] objectAtIndex:0] frame].size.height - lastMousePosition.y);
845     CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition);
846     CGEventSetIntegerValueField(cgScrollEvent, kCGScrollWheelEventIsContinuous, 1);
847     CGEventSetIntegerValueField(cgScrollEvent, kCGScrollWheelEventScrollPhase, phase);
848     CGEventSetIntegerValueField(cgScrollEvent, kCGScrollWheelEventMomentumPhase, momentum);
849     
850     NSEvent* scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent];
851     CFRelease(cgScrollEvent);
852
853     if (NSView* targetView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]]) {
854         [NSApp _setCurrentEvent:scrollEvent];
855         [targetView scrollWheel:scrollEvent];
856         [NSApp _setCurrentEvent:nil];
857     } else
858         printf("mouseScrollByX...andMomentumPhases: Unable to locate target view for current mouse location.");
859 #endif
860 }
861
862 - (NSArray *)contextClick
863 {
864 #if PLATFORM(MAC)
865     [[[mainFrame frameView] documentView] layout];
866     [self updateClickCountForButton:RightMouseButton];
867
868     NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown
869                                         location:lastMousePosition 
870                                    modifierFlags:0 
871                                        timestamp:[self currentEventTime]
872                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
873                                          context:[NSGraphicsContext currentContext] 
874                                      eventNumber:++eventNumber 
875                                       clickCount:clickCount 
876                                         pressure:0.0];
877
878     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
879     NSMutableArray *menuItemStrings = [NSMutableArray array];
880     
881     if (subView) {
882         [NSApp _setCurrentEvent:event];
883         NSMenu* menu = [subView menuForEvent:event];
884         [NSApp _setCurrentEvent:nil];
885
886         for (int i = 0; i < [menu numberOfItems]; ++i) {
887             NSMenuItem* menuItem = [menu itemAtIndex:i];
888             if (!strcmp("Inspect Element", [[menuItem title] UTF8String]))
889                 continue;
890
891             if ([menuItem isSeparatorItem])
892                 [menuItemStrings addObject:@"<separator>"];
893             else
894                 [menuItemStrings addObject:[menuItem title]];
895         }
896     }
897     
898     return menuItemStrings;
899 #else
900     return nil;
901 #endif
902 }
903
904 - (void)scheduleAsynchronousClick
905 {
906     [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0];
907     [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0];
908 }
909
910 + (void)saveEvent:(NSInvocation *)event
911 {
912     if (!savedMouseEvents)
913         savedMouseEvents = [[NSMutableArray alloc] init];
914     [savedMouseEvents addObject:event];
915 }
916
917 + (void)replaySavedEvents
918 {
919     replayingSavedEvents = YES;
920     while ([savedMouseEvents count]) {
921         // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate
922         NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease];
923         [savedMouseEvents removeObjectAtIndex:0];
924         [invocation invoke];
925     }
926     replayingSavedEvents = NO;
927 }
928
929 + (void)clearSavedEvents
930 {
931     [savedMouseEvents release];
932     savedMouseEvents = nil;
933 }
934
935 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
936 {
937     NSString *eventCharacter = character;
938     unsigned short keyCode = 0;
939     if ([character isEqualToString:@"leftArrow"]) {
940         const unichar ch = NSLeftArrowFunctionKey;
941         eventCharacter = [NSString stringWithCharacters:&ch length:1];
942         keyCode = 0x7B;
943     } else if ([character isEqualToString:@"rightArrow"]) {
944         const unichar ch = NSRightArrowFunctionKey;
945         eventCharacter = [NSString stringWithCharacters:&ch length:1];
946         keyCode = 0x7C;
947     } else if ([character isEqualToString:@"upArrow"]) {
948         const unichar ch = NSUpArrowFunctionKey;
949         eventCharacter = [NSString stringWithCharacters:&ch length:1];
950         keyCode = 0x7E;
951     } else if ([character isEqualToString:@"downArrow"]) {
952         const unichar ch = NSDownArrowFunctionKey;
953         eventCharacter = [NSString stringWithCharacters:&ch length:1];
954         keyCode = 0x7D;
955     } else if ([character isEqualToString:@"pageUp"]) {
956         const unichar ch = NSPageUpFunctionKey;
957         eventCharacter = [NSString stringWithCharacters:&ch length:1];
958         keyCode = 0x74;
959     } else if ([character isEqualToString:@"pageDown"]) {
960         const unichar ch = NSPageDownFunctionKey;
961         eventCharacter = [NSString stringWithCharacters:&ch length:1];
962         keyCode = 0x79;
963     } else if ([character isEqualToString:@"home"]) {
964         const unichar ch = NSHomeFunctionKey;
965         eventCharacter = [NSString stringWithCharacters:&ch length:1];
966         keyCode = 0x73;
967     } else if ([character isEqualToString:@"end"]) {
968         const unichar ch = NSEndFunctionKey;
969         eventCharacter = [NSString stringWithCharacters:&ch length:1];
970         keyCode = 0x77;
971     } else if ([character isEqualToString:@"insert"]) {
972         const unichar ch = NSInsertFunctionKey;
973         eventCharacter = [NSString stringWithCharacters:&ch length:1];
974         keyCode = 0x72;
975     } else if ([character isEqualToString:@"delete"]) {
976         const unichar ch = NSDeleteFunctionKey;
977         eventCharacter = [NSString stringWithCharacters:&ch length:1];
978         keyCode = 0x75;
979     } else if ([character isEqualToString:@"escape"]) {
980         const unichar ch = 0x1B;
981         eventCharacter = [NSString stringWithCharacters:&ch length:1];
982         keyCode = 0x35;
983     } else if ([character isEqualToString:@"printScreen"]) {
984         const unichar ch = NSPrintScreenFunctionKey;
985         eventCharacter = [NSString stringWithCharacters:&ch length:1];
986         keyCode = 0x0; // There is no known virtual key code for PrintScreen.
987     } else if ([character isEqualToString:@"cyrillicSmallLetterA"]) {
988         const unichar ch = 0x0430;
989         eventCharacter = [NSString stringWithCharacters:&ch length:1];
990         keyCode = 0x3; // Shares key with "F" on Russian layout.
991     } else if ([character isEqualToString:@"leftControl"]) {
992         const unichar ch = 0xFFE3;
993         eventCharacter = [NSString stringWithCharacters:&ch length:1];
994         keyCode = 0x3B;
995     } else if ([character isEqualToString:@"leftShift"]) {
996         const unichar ch = 0xFFE1;
997         eventCharacter = [NSString stringWithCharacters:&ch length:1];
998         keyCode = 0x38;
999     } else if ([character isEqualToString:@"leftAlt"]) {
1000         const unichar ch = 0xFFE7;
1001         eventCharacter = [NSString stringWithCharacters:&ch length:1];
1002         keyCode = 0x3A;
1003     } else if ([character isEqualToString:@"rightControl"]) {
1004         const unichar ch = 0xFFE4;
1005         eventCharacter = [NSString stringWithCharacters:&ch length:1];
1006         keyCode = 0x3E;
1007     } else if ([character isEqualToString:@"rightShift"]) {
1008         const unichar ch = 0xFFE2;
1009         eventCharacter = [NSString stringWithCharacters:&ch length:1];
1010         keyCode = 0x3C;
1011     } else if ([character isEqualToString:@"rightAlt"]) {
1012         const unichar ch = 0xFFE8;
1013         eventCharacter = [NSString stringWithCharacters:&ch length:1];
1014         keyCode = 0x3D;
1015     }
1016
1017     // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
1018     // If the input string is a function-key name, set its key code.
1019     for (unsigned i = 1; i <= 24; i++) {
1020         if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
1021             const unichar ch = NSF1FunctionKey + (i - 1);
1022             eventCharacter = [NSString stringWithCharacters:&ch length:1];
1023             switch (i) {
1024                 case 1: keyCode = 0x7A; break;
1025                 case 2: keyCode = 0x78; break;
1026                 case 3: keyCode = 0x63; break;
1027                 case 4: keyCode = 0x76; break;
1028                 case 5: keyCode = 0x60; break;
1029                 case 6: keyCode = 0x61; break;
1030                 case 7: keyCode = 0x62; break;
1031                 case 8: keyCode = 0x64; break;
1032                 case 9: keyCode = 0x65; break;
1033                 case 10: keyCode = 0x6D; break;
1034                 case 11: keyCode = 0x67; break;
1035                 case 12: keyCode = 0x6F; break;
1036                 case 13: keyCode = 0x69; break;
1037                 case 14: keyCode = 0x6B; break;
1038                 case 15: keyCode = 0x71; break;
1039                 case 16: keyCode = 0x6A; break;
1040                 case 17: keyCode = 0x40; break;
1041                 case 18: keyCode = 0x4F; break;
1042                 case 19: keyCode = 0x50; break;
1043                 case 20: keyCode = 0x5A; break;
1044             }
1045         }
1046     }
1047
1048     // FIXME: No keyCode is set for most keys.
1049     if ([character isEqualToString:@"\t"])
1050         keyCode = 0x30;
1051     else if ([character isEqualToString:@" "])
1052         keyCode = 0x31;
1053     else if ([character isEqualToString:@"\r"])
1054         keyCode = 0x24;
1055     else if ([character isEqualToString:@"\n"])
1056         keyCode = 0x4C;
1057     else if ([character isEqualToString:@"\x8"])
1058         keyCode = 0x33;
1059     else if ([character isEqualToString:@"a"])
1060         keyCode = 0x00;
1061     else if ([character isEqualToString:@"b"])
1062         keyCode = 0x0B;
1063     else if ([character isEqualToString:@"d"])
1064         keyCode = 0x02;
1065     else if ([character isEqualToString:@"e"])
1066         keyCode = 0x0E;
1067     else if ([character isEqualToString:@"\x1b"])
1068         keyCode = 0x1B;
1069
1070     KeyMappingEntry table[] = {
1071         {0x2F, 0x41, '.', nil},
1072         {0,    0x43, '*', nil},
1073         {0,    0x45, '+', nil},
1074         {0,    0x47, NSClearLineFunctionKey, @"clear"},
1075         {0x2C, 0x4B, '/', nil},
1076         {0,    0x4C, 3, @"enter" },
1077         {0x1B, 0x4E, '-', nil},
1078         {0x18, 0x51, '=', nil},
1079         {0x1D, 0x52, '0', nil},
1080         {0x12, 0x53, '1', nil},
1081         {0x13, 0x54, '2', nil},
1082         {0x14, 0x55, '3', nil},
1083         {0x15, 0x56, '4', nil},
1084         {0x17, 0x57, '5', nil},
1085         {0x16, 0x58, '6', nil},
1086         {0x1A, 0x59, '7', nil},
1087         {0x1C, 0x5B, '8', nil},
1088         {0x19, 0x5C, '9', nil},
1089     };
1090     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) {
1091         NSString* currentCharacterString = [NSString stringWithCharacters:&table[i].character length:1];
1092         if ([character isEqualToString:currentCharacterString] || [character isEqualToString:table[i].characterName]) {
1093             if (location == DOM_KEY_LOCATION_NUMPAD)
1094                 keyCode = table[i].macNumpadKeyCode;
1095             else
1096                 keyCode = table[i].macKeyCode;
1097             eventCharacter = currentCharacterString;
1098             break;
1099         }
1100     }
1101
1102     NSString *charactersIgnoringModifiers = eventCharacter;
1103
1104     int modifierFlags = 0;
1105
1106     if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
1107 #if !PLATFORM(IOS)
1108         modifierFlags |= NSEventModifierFlagShift;
1109 #else
1110         modifierFlags |= WebEventFlagMaskAlphaShift;
1111 #endif
1112         charactersIgnoringModifiers = [character lowercaseString];
1113     }
1114
1115     modifierFlags |= buildModifierFlags(modifiers);
1116
1117     if (location == DOM_KEY_LOCATION_NUMPAD)
1118         modifierFlags |= NSEventModifierFlagNumericPad;
1119
1120     [[[mainFrame frameView] documentView] layout];
1121
1122 #if !PLATFORM(IOS)
1123     NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1124                         location:NSMakePoint(5, 5)
1125                         modifierFlags:modifierFlags
1126                         timestamp:[self currentEventTime]
1127                         windowNumber:[[[mainFrame webView] window] windowNumber]
1128                         context:[NSGraphicsContext currentContext]
1129                         characters:eventCharacter
1130                         charactersIgnoringModifiers:charactersIgnoringModifiers
1131                         isARepeat:NO
1132                         keyCode:keyCode];
1133 #else
1134     WebEvent *event = [[WebEvent alloc] initWithKeyEventType:WebEventKeyDown
1135                         timeStamp:[self currentEventTime]
1136                         characters:eventCharacter
1137                         charactersIgnoringModifiers:charactersIgnoringModifiers
1138                         modifiers:(WebEventFlags)modifierFlags
1139                         isRepeating:NO
1140                         withFlags:0
1141                         keyCode:[character characterAtIndex:0]
1142                         isTabKey:([character characterAtIndex:0] == '\t')
1143                         characterSet:WebEventCharacterSetASCII];
1144 #endif
1145
1146 #if !PLATFORM(IOS)
1147     [NSApp _setCurrentEvent:event];
1148 #endif
1149     [[[[mainFrame webView] window] firstResponder] keyDown:event];
1150 #if !PLATFORM(IOS)
1151     [NSApp _setCurrentEvent:nil];
1152 #endif
1153
1154 #if !PLATFORM(IOS)
1155     event = [NSEvent keyEventWithType:NSEventTypeKeyUp
1156                         location:NSMakePoint(5, 5)
1157                         modifierFlags:modifierFlags
1158                         timestamp:[self currentEventTime]
1159                         windowNumber:[[[mainFrame webView] window] windowNumber]
1160                         context:[NSGraphicsContext currentContext]
1161                         characters:eventCharacter
1162                         charactersIgnoringModifiers:charactersIgnoringModifiers
1163                         isARepeat:NO
1164                         keyCode:keyCode];
1165 #else
1166     [event release];
1167     event = [[WebEvent alloc] initWithKeyEventType:WebEventKeyUp
1168                         timeStamp:[self currentEventTime]
1169                         characters:eventCharacter
1170                         charactersIgnoringModifiers:charactersIgnoringModifiers
1171                         modifiers:(WebEventFlags)modifierFlags
1172                         isRepeating:NO
1173                         withFlags:0
1174                         keyCode:[character characterAtIndex:0]
1175                         isTabKey:([character characterAtIndex:0] == '\t')
1176                         characterSet:WebEventCharacterSetASCII];
1177 #endif
1178
1179 #if !PLATFORM(IOS)
1180     [NSApp _setCurrentEvent:event];
1181 #endif
1182     [[[[mainFrame webView] window] firstResponder] keyUp:event];
1183 #if !PLATFORM(IOS)
1184     [NSApp _setCurrentEvent:nil];
1185 #endif
1186
1187 #if PLATFORM(IOS)
1188     [event release];
1189 #endif
1190 }
1191
1192 - (void)keyDownWrapper:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
1193 {
1194     [self keyDown:character withModifiers:modifiers withLocation:location];
1195 }
1196
1197 - (void)scheduleAsynchronousKeyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
1198 {
1199     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(keyDownWrapper:withModifiers:withLocation:)]];
1200     [invocation retainArguments];
1201     [invocation setTarget:self];
1202     [invocation setSelector:@selector(keyDownWrapper:withModifiers:withLocation:)];
1203     [invocation setArgument:&character atIndex:2];
1204     [invocation setArgument:&modifiers atIndex:3];
1205     [invocation setArgument:&location atIndex:4];
1206     [invocation performSelector:@selector(invoke) withObject:nil afterDelay:0];
1207 }
1208
1209 - (void)enableDOMUIEventLogging:(WebScriptObject *)node
1210 {
1211     NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator];
1212     id eventName;
1213     while ((eventName = [eventEnumerator nextObject])) {
1214         [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO];
1215     }
1216 }
1217
1218 - (void)handleEvent:(DOMEvent *)event
1219 {
1220     DOMNode *target = [event target];
1221
1222     printf("event type:      %s\n", [[event type] UTF8String]);
1223     printf("  target:        <%s>\n", [[[target nodeName] lowercaseString] UTF8String]);
1224     
1225     if ([event isKindOfClass:[DOMEvent class]]) {
1226         printf("  eventPhase:    %d\n", [event eventPhase]);
1227         printf("  bubbles:       %d\n", [event bubbles] ? 1 : 0);
1228         printf("  cancelable:    %d\n", [event cancelable] ? 1 : 0);
1229     }
1230     
1231     if ([event isKindOfClass:[DOMUIEvent class]]) {
1232         printf("  detail:        %d\n", [(DOMUIEvent*)event detail]);
1233         
1234         DOMAbstractView *view = [(DOMUIEvent*)event view];
1235         if (view) {
1236             printf("  view:          OK");            
1237             if ([view document])
1238                 printf(" (document: OK)");
1239             printf("\n");
1240         }
1241     }
1242     
1243     if ([event isKindOfClass:[DOMKeyboardEvent class]]) {
1244         printf("  keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]);
1245         printf("  keyLocation:   %d\n", [(DOMKeyboardEvent*)event location]);
1246         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
1247                [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, 
1248                [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, 
1249                [(DOMKeyboardEvent*)event altKey] ? 1 : 0, 
1250                [(DOMKeyboardEvent*)event metaKey] ? 1 : 0);
1251         printf("  keyCode:       %d\n", [(DOMKeyboardEvent*)event keyCode]);
1252         printf("  charCode:      %d\n", [(DOMKeyboardEvent*)event charCode]);
1253     }
1254     
1255     if ([event isKindOfClass:[DOMMouseEvent class]]) {
1256         printf("  button:        %d\n", [(DOMMouseEvent*)event button]);
1257         printf("  clientX:       %d\n", [(DOMMouseEvent*)event clientX]);
1258         printf("  clientY:       %d\n", [(DOMMouseEvent*)event clientY]);
1259         printf("  screenX:       %d\n", [(DOMMouseEvent*)event screenX]);
1260         printf("  screenY:       %d\n", [(DOMMouseEvent*)event screenY]);
1261         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
1262                [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, 
1263                [(DOMMouseEvent*)event shiftKey] ? 1 : 0, 
1264                [(DOMMouseEvent*)event altKey] ? 1 : 0, 
1265                [(DOMMouseEvent*)event metaKey] ? 1 : 0);
1266         id relatedTarget = [(DOMMouseEvent*)event relatedTarget];
1267         if (relatedTarget) {
1268             printf("  relatedTarget: %s", [[[relatedTarget class] description] UTF8String]);
1269             if ([relatedTarget isKindOfClass:[DOMNode class]])
1270                 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]);
1271             printf("\n");
1272         }
1273     }
1274     
1275     if ([event isKindOfClass:[DOMMutationEvent class]]) {
1276         printf("  prevValue:     %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]);
1277         printf("  newValue:      %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]);
1278         printf("  attrName:      %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]);
1279         printf("  attrChange:    %d\n", [(DOMMutationEvent*)event attrChange]);
1280         DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode];
1281         if (relatedNode) {
1282             printf("  relatedNode:   %s (nodeName: %s)\n", 
1283                    [[[relatedNode class] description] UTF8String],
1284                    [[relatedNode nodeName] UTF8String]);
1285         }
1286     }
1287     
1288     if ([event isKindOfClass:[DOMWheelEvent class]]) {
1289         printf("  clientX:       %d\n", [(DOMWheelEvent*)event clientX]);
1290         printf("  clientY:       %d\n", [(DOMWheelEvent*)event clientY]);
1291         printf("  screenX:       %d\n", [(DOMWheelEvent*)event screenX]);
1292         printf("  screenY:       %d\n", [(DOMWheelEvent*)event screenY]);
1293         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
1294                [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, 
1295                [(DOMWheelEvent*)event shiftKey] ? 1 : 0, 
1296                [(DOMWheelEvent*)event altKey] ? 1 : 0, 
1297                [(DOMWheelEvent*)event metaKey] ? 1 : 0);
1298         printf("  isHorizontal:  %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0);
1299         printf("  wheelDelta:    %d\n", [(DOMWheelEvent*)event wheelDelta]);
1300     }
1301 }
1302
1303 // FIXME: It's not good to have a test hard-wired into this controller like this.
1304 // Instead we need to get testing framework based on the Objective-C bindings
1305 // to work well enough that we can test that way instead.
1306 - (void)fireKeyboardEventsToElement:(WebScriptObject *)element {
1307     
1308     if (![element isKindOfClass:[DOMHTMLElement class]])
1309         return;
1310     
1311     DOMHTMLElement *target = (DOMHTMLElement*)element;
1312     DOMDocument *document = [target ownerDocument];
1313     
1314     // Keyboard Event 1
1315     
1316     DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"];
1317     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" 
1318                                          canBubble:YES
1319                                         cancelable:YES
1320                                               view:[document defaultView]
1321                                      keyIdentifier:@"U+000041" 
1322                                        location:0
1323                                            ctrlKey:YES
1324                                             altKey:NO
1325                                           shiftKey:NO
1326                                            metaKey:NO];
1327     [target dispatchEvent:domEvent];  
1328         
1329     // Keyboard Event 2
1330     
1331     domEvent = [document createEvent:@"KeyboardEvent"];
1332     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" 
1333                                          canBubble:YES
1334                                         cancelable:YES
1335                                               view:[document defaultView]
1336                                      keyIdentifier:@"U+000045" 
1337                                        location:1
1338                                            ctrlKey:NO
1339                                             altKey:YES
1340                                           shiftKey:NO
1341                                            metaKey:NO];
1342     [target dispatchEvent:domEvent];    
1343     
1344     // Keyboard Event 3
1345     
1346     domEvent = [document createEvent:@"KeyboardEvent"];
1347     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" 
1348                                          canBubble:YES
1349                                         cancelable:YES
1350                                               view:[document defaultView]
1351                                      keyIdentifier:@"U+000056" 
1352                                        location:0
1353                                            ctrlKey:NO
1354                                             altKey:NO
1355                                           shiftKey:NO
1356                                            metaKey:NO];
1357     [target dispatchEvent:domEvent];   
1358     
1359 }
1360
1361 - (void)monitorWheelEvents
1362 {
1363 #if PLATFORM(MAC)
1364     WebCore::Frame* frame = [[mainFrame webView] _mainCoreFrame];
1365     if (!frame)
1366         return;
1367
1368     WebCoreTestSupport::monitorWheelEvents(*frame);
1369 #endif
1370 }
1371
1372 - (void)callAfterScrollingCompletes:(WebScriptObject*)callback
1373 {
1374 #if PLATFORM(MAC)
1375     JSObjectRef jsCallbackFunction = [callback JSObject];
1376     if (!jsCallbackFunction)
1377         return;
1378
1379     WebCore::Frame* frame = [[mainFrame webView] _mainCoreFrame];
1380     if (!frame)
1381         return;
1382
1383     JSGlobalContextRef globalContext = [mainFrame globalContext];
1384     WebCoreTestSupport::setTestCallbackAndStartNotificationTimer(*frame, globalContext, jsCallbackFunction);
1385 #endif
1386 }
1387
1388 #if PLATFORM(IOS)
1389 - (void)addTouchAtX:(int)x y:(int)y
1390 {
1391     if (!touches)
1392         touches = [[NSMutableArray alloc] init];
1393
1394     [touches addObject:[SyntheticTouch touchWithLocation:CGPointMake(x, y) phase:UITouchPhaseBegan identifier:currentTouchIdentifier++]];
1395 }
1396
1397 - (void)cancelTouchAtIndex:(unsigned)index
1398 {
1399     if (index < [touches count])
1400         [[touches objectAtIndex:index] setPhase:UITouchPhaseCancelled];
1401 }
1402
1403 - (void)clearTouchPoints
1404 {
1405     [touches removeAllObjects];
1406 }
1407
1408 - (void)releaseTouchAtIndex:(unsigned)index
1409 {
1410     if (index < [touches count]) {
1411         SyntheticTouch *touch = [touches objectAtIndex:index];
1412         [touch setPhase:UITouchPhaseEnded];
1413     }
1414 }
1415
1416 - (void)markAllTouchesAsStationary
1417 {
1418     for (SyntheticTouch *touch in touches)
1419         [touch setPhase:UITouchPhaseStationary];
1420 }
1421
1422 - (void)updateTouchAtIndex:(unsigned)index x:(int)x y:(int)y
1423 {
1424     if (index < [touches count]) {
1425         SyntheticTouch *touch = [touches objectAtIndex:index];
1426         [touch setPhase:UITouchPhaseMoved];
1427         [touch setLocation:CGPointMake(x, y)];
1428     }
1429 }
1430
1431 - (void)setTouchModifier:(NSString*)modifierName value:(BOOL)flag
1432 {
1433     unsigned modifier = 0;
1434     
1435     if ([modifierName isEqualToString:@"alt"])
1436         modifier = WebEventFlagMaskAlternate;
1437     else if ([modifierName isEqualToString:@"shift"])
1438         modifier = WebEventFlagMaskShift;
1439     else if ([modifierName isEqualToString:@"meta"])
1440         modifier = WebEventFlagMaskCommand;
1441     else if ([modifierName isEqualToString:@"ctrl"])
1442         modifier = WebEventFlagMaskControl;
1443
1444     if (!modifier)
1445         return;
1446
1447     if (flag)
1448         nextEventFlags |= modifier;
1449     else
1450         nextEventFlags &= ~modifier;
1451 }
1452
1453 - (void)sentTouchEventOfType:(WebEventType)type
1454 {
1455     NSMutableArray *touchLocations = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1456     NSMutableArray *touchIdentifiers = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1457     NSMutableArray *touchPhases = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1458     
1459     CGPoint centroid = CGPointZero;
1460     NSUInteger touchesDownCount = 0;
1461
1462     for (SyntheticTouch *currTouch in touches) {
1463         [touchLocations addObject:[NSValue valueWithCGPoint:currTouch.location]];
1464         [touchIdentifiers addObject:[NSNumber numberWithUnsignedInt:currTouch.identifier]];
1465         [touchPhases addObject:[NSNumber numberWithUnsignedInt:currTouch.phase]];
1466
1467         if ((currTouch.phase == UITouchPhaseEnded) || (currTouch.phase == UITouchPhaseCancelled))
1468             continue;
1469         
1470         centroid.x += currTouch.location.x;
1471         centroid.y += currTouch.location.y;
1472
1473         touchesDownCount++;
1474     }
1475
1476     if (touchesDownCount > 0)
1477         centroid = CGPointMake(centroid.x / touchesDownCount, centroid.y / touchesDownCount);
1478     else
1479         centroid = CGPointZero;
1480
1481     WebEvent *event = [[WebEvent alloc] initWithTouchEventType:type
1482                                         timeStamp:[self currentEventTime]
1483                                         location:centroid
1484                                         modifiers:(WebEventFlags)nextEventFlags
1485                                         touchCount:[touches count]
1486                                         touchLocations:touchLocations
1487                                         touchIdentifiers:touchIdentifiers
1488                                         touchPhases:touchPhases
1489                                         isGesture:(touchesDownCount > 1)
1490                                         gestureScale:1
1491                                         gestureRotation:0];
1492     // Ensure that layout is up-to-date so that hit-testing through WAKViews works correctly.
1493     [mainFrame updateLayout];
1494     [[[mainFrame webView] window] sendEventSynchronously:event];
1495     [event release];
1496     
1497     [touchLocations release];
1498     [touchIdentifiers release];
1499     [touchPhases release];
1500     
1501     nextEventFlags = 0;
1502 }
1503
1504 - (void)touchStart
1505 {
1506     [self sentTouchEventOfType:WebEventTouchBegin];
1507 }
1508
1509 - (void)touchMove
1510 {
1511     [self sentTouchEventOfType:WebEventTouchChange];
1512 }
1513
1514 - (void)touchEnd
1515 {
1516     [self sentTouchEventOfType:WebEventTouchEnd];
1517
1518     NSMutableArray *touchesToRemove = [[NSMutableArray alloc] init];
1519     for (SyntheticTouch *currTouch in touches) {
1520         if (currTouch.phase == UITouchPhaseEnded)
1521             [touchesToRemove addObject:currTouch];
1522     }
1523
1524     [touches removeObjectsInArray:touchesToRemove];
1525     [touchesToRemove release];
1526 }
1527
1528 - (void)touchCancel
1529 {
1530     [self sentTouchEventOfType:WebEventTouchCancel];
1531
1532     NSMutableArray *touchesToRemove = [[NSMutableArray alloc] init];
1533     for (SyntheticTouch *currTouch in touches) {
1534         if (currTouch.phase == UITouchPhaseCancelled)
1535             [touchesToRemove addObject:currTouch];
1536     }
1537
1538     [touches removeObjectsInArray:touchesToRemove];
1539     [touchesToRemove release];
1540 }
1541 #endif
1542
1543 @end