One esc to exit fullscreen and pointer lock
[WebKit-https.git] / Tools / WebKitTestRunner / mac / EventSenderProxy.mm
1 /*
2  * Copyright (C) 2011, 2014-2015 Apple Inc. All rights reserved.
3  * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #import "config.h"
28 #import "EventSenderProxy.h"
29
30 #import "PlatformWebView.h"
31 #import "StringFunctions.h"
32 #import "TestController.h"
33 #import "TestRunnerWKWebView.h"
34 #import <Carbon/Carbon.h>
35 #import <WebKit/WKString.h>
36 #import <WebKit/WKPagePrivate.h>
37 #import <WebKit/WKWebView.h>
38 #import <wtf/RetainPtr.h>
39 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
40
41 @interface NSApplication (Details)
42 - (void)_setCurrentEvent:(NSEvent *)event;
43 @end
44
45 @interface NSEvent (ForTestRunner)
46 - (void)_postDelayed;
47 @end
48
49 #if defined(__LP64__)
50 struct WKTRCGSEventRecord {
51     char offset1[150];
52     uint8_t phase;
53     char offset2[13];
54     float deltaX;
55     float deltaY;
56     char offset3[76];
57 } __attribute__((packed));
58 #endif
59
60 @interface EventSenderSyntheticEvent : NSEvent {
61 @public
62     NSPoint _eventSender_locationInWindow;
63     NSPoint _eventSender_location;
64     NSInteger _eventSender_stage;
65     float _eventSender_pressure;
66     CGFloat _eventSender_stageTransition;
67     NSEventPhase _eventSender_phase;
68     NSEventPhase _eventSender_momentumPhase;
69     NSTimeInterval _eventSender_timestamp;
70     NSInteger _eventSender_eventNumber;
71     short _eventSender_subtype;
72     NSEventType _eventSender_type;
73     NSWindow *_eventSender_window;
74
75
76 #if defined(__LP64__)
77     WKTRCGSEventRecord _eventSender_cgsEventRecord;
78 #endif
79 }
80
81 - (id)initPressureEventAtLocation:(NSPoint)location globalLocation:(NSPoint)globalLocation stage:(NSInteger)stage pressure:(float)pressure stageTransition:(float)stageTransition phase:(NSEventPhase)phase time:(NSTimeInterval)time eventNumber:(NSInteger)eventNumber window:(NSWindow *)window;
82 - (NSTimeInterval)timestamp;
83 @end
84
85 @implementation EventSenderSyntheticEvent
86
87 - (id)initPressureEventAtLocation:(NSPoint)location globalLocation:(NSPoint)globalLocation stage:(NSInteger)stage pressure:(float)pressure stageTransition:(float)stageTransition phase:(NSEventPhase)phase time:(NSTimeInterval)time eventNumber:(NSInteger)eventNumber window:(NSWindow *)window
88 {
89     self = [super init];
90
91     if (!self)
92         return nil;
93
94     _eventSender_location = location;
95     _eventSender_locationInWindow = globalLocation;
96     _eventSender_stage = stage;
97     _eventSender_pressure = pressure;
98     _eventSender_stageTransition = stageTransition;
99     _eventSender_phase = phase;
100     _eventSender_timestamp = time;
101     _eventSender_eventNumber = eventNumber;
102     _eventSender_window = window;
103 #if defined(__LP64__) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101003
104     self->_type = NSEventTypePressure;
105     _eventSender_type = NSEventTypePressure;
106 #endif
107
108     return self;
109 }
110
111 - (CGFloat)stageTransition
112 {
113     return _eventSender_stageTransition;
114 }
115
116 - (NSTimeInterval)timestamp
117 {
118     return _eventSender_timestamp;
119 }
120
121 - (NSEventType)type
122 {
123     return _eventSender_type;
124 }
125
126 - (NSEventSubtype)subtype
127 {
128     return (NSEventSubtype)_eventSender_subtype;
129 }
130
131 - (NSPoint)locationInWindow
132 {
133     return _eventSender_location;
134 }
135
136 - (NSPoint)location
137 {
138     return _eventSender_locationInWindow;
139 }
140
141 - (NSInteger)stage
142 {
143     return _eventSender_stage;
144 }
145
146 - (float)pressure
147 {
148     return _eventSender_pressure;
149 }
150
151 - (NSEventPhase)phase
152 {
153     return _eventSender_phase;
154 }
155
156 - (NSEventPhase)momentumPhase
157 {
158     return _eventSender_momentumPhase;
159 }
160
161 - (NSInteger)eventNumber
162 {
163     return _eventSender_eventNumber;
164 }
165
166 - (BOOL)_isTouchesEnded
167 {
168     return false;
169 }
170
171 #if defined(__LP64__)
172 - (WKTRCGSEventRecord)_cgsEventRecord
173 {
174     return _eventSender_cgsEventRecord;
175 }
176 #endif
177
178 - (NSWindow *)window
179 {
180     return _eventSender_window;
181 }
182
183 @end
184
185 namespace WTR {
186
187 enum MouseAction {
188     MouseDown,
189     MouseUp,
190     MouseDragged
191 };
192
193 // Match the DOM spec (sadly the DOM spec does not provide an enum)
194 enum MouseButton {
195     LeftMouseButton = 0,
196     MiddleMouseButton = 1,
197     RightMouseButton = 2,
198     NoMouseButton = -1
199 };
200
201 struct KeyMappingEntry {
202     int macKeyCode;
203     int macNumpadKeyCode;
204     unichar character;
205     NSString* characterName;
206 };
207
208 static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action)
209 {
210     switch (button) {
211     case LeftMouseButton:
212         switch (action) {
213         case MouseDown:
214             return NSEventTypeLeftMouseDown;
215         case MouseUp:
216             return NSEventTypeLeftMouseUp;
217         case MouseDragged:
218             return NSEventTypeLeftMouseDragged;
219         }
220     case RightMouseButton:
221         switch (action) {
222         case MouseDown:
223             return NSEventTypeRightMouseDown;
224         case MouseUp:
225             return NSEventTypeRightMouseUp;
226         case MouseDragged:
227             return NSEventTypeRightMouseDragged;
228         }
229     default:
230         switch (action) {
231         case MouseDown:
232             return NSEventTypeOtherMouseDown;
233         case MouseUp:
234             return NSEventTypeOtherMouseUp;
235         case MouseDragged:
236             return NSEventTypeOtherMouseDragged;
237         }
238     }
239     assert(0);
240     return static_cast<NSEventType>(0);
241 }
242
243 static int buildModifierFlags(WKEventModifiers modifiers)
244 {
245     int flags = 0;
246     if (modifiers & kWKEventModifiersControlKey)
247         flags |= NSEventModifierFlagControl;
248     if (modifiers & kWKEventModifiersShiftKey)
249         flags |= NSEventModifierFlagShift;
250     if (modifiers & kWKEventModifiersAltKey)
251         flags |= NSEventModifierFlagOption;
252     if (modifiers & kWKEventModifiersMetaKey)
253         flags |= NSEventModifierFlagCommand;
254     if (modifiers & kWKEventModifiersCapsLockKey)
255         flags |= NSEventModifierFlagCapsLock;
256     return flags;
257 }
258
259 static NSTimeInterval absoluteTimeForEventTime(double currentEventTime)
260 {
261     return GetCurrentEventTime() + currentEventTime;
262 }
263
264 EventSenderProxy::EventSenderProxy(TestController* testController)
265     : m_testController(testController)
266     , m_time(0)
267     , m_position()
268     , m_leftMouseButtonDown(false)
269     , m_clickCount(0)
270     , m_clickTime(0)
271     , m_clickPosition()
272     , m_clickButton(kWKEventMouseButtonNoButton)
273     , eventNumber(0)
274 {
275 }
276
277 EventSenderProxy::~EventSenderProxy()
278 {
279 }
280
281 void EventSenderProxy::updateClickCountForButton(int button)
282 {
283     if (m_time - m_clickTime < 1 && m_position == m_clickPosition && button == m_clickButton) {
284         ++m_clickCount;
285         m_clickTime = m_time;
286         return;
287     }
288
289     m_clickCount = 1;
290     m_clickTime = m_time;
291     m_clickPosition = m_position;
292     m_clickButton = button;
293 }
294
295 void EventSenderProxy::mouseDown(unsigned buttonNumber, WKEventModifiers modifiers)
296 {
297     updateClickCountForButton(buttonNumber);
298
299     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
300     NSEvent *event = [NSEvent mouseEventWithType:eventType
301                                         location:NSMakePoint(m_position.x, m_position.y)
302                                    modifierFlags:buildModifierFlags(modifiers)
303                                        timestamp:absoluteTimeForEventTime(currentEventTime())
304                                     windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
305                                          context:[NSGraphicsContext currentContext] 
306                                      eventNumber:++eventNumber 
307                                       clickCount:m_clickCount 
308                                         pressure:0.0];
309
310     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]];
311     if (targetView) {
312         [NSApp _setCurrentEvent:event];
313         [targetView mouseDown:event];
314         [NSApp _setCurrentEvent:nil];
315         if (buttonNumber == LeftMouseButton)
316             m_leftMouseButtonDown = true;
317     }
318 }
319
320 void EventSenderProxy::mouseUp(unsigned buttonNumber, WKEventModifiers modifiers)
321 {
322     NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
323     NSEvent *event = [NSEvent mouseEventWithType:eventType
324                                         location:NSMakePoint(m_position.x, m_position.y)
325                                    modifierFlags:buildModifierFlags(modifiers)
326                                        timestamp:absoluteTimeForEventTime(currentEventTime())
327                                     windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
328                                          context:[NSGraphicsContext currentContext] 
329                                      eventNumber:++eventNumber 
330                                       clickCount:m_clickCount 
331                                         pressure:0.0];
332
333     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]];
334     // FIXME: Silly hack to teach WKTR to respect capturing mouse events outside the WKView.
335     // The right solution is just to use NSApplication's built-in event sending methods, 
336     // instead of rolling our own algorithm for selecting an event target.
337     if (!targetView)
338         targetView = m_testController->mainWebView()->platformView();
339
340     ASSERT(targetView);
341     [NSApp _setCurrentEvent:event];
342     [targetView mouseUp:event];
343     [NSApp _setCurrentEvent:nil];
344     if (buttonNumber == LeftMouseButton)
345         m_leftMouseButtonDown = false;
346     m_clickTime = currentEventTime();
347     m_clickPosition = m_position;
348 }
349
350 #if defined(__LP64__) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101003
351 void EventSenderProxy::sendMouseDownToStartPressureEvents()
352 {
353     updateClickCountForButton(0);
354
355     NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
356         location:NSMakePoint(m_position.x, m_position.y)
357         modifierFlags:NSEventMaskPressure
358         timestamp:absoluteTimeForEventTime(currentEventTime())
359         windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
360         context:[NSGraphicsContext currentContext]
361         eventNumber:++eventNumber
362         clickCount:m_clickCount
363         pressure:0.0];
364
365     [NSApp sendEvent:event];
366 }
367
368 static void handleForceEventSynchronously(NSEvent *event)
369 {
370     // Force events have to be pushed onto the queue, then popped off right away and handled synchronously in order
371     // to get the NSImmediateActionGestureRecognizer to do the right thing.
372     [event _postDelayed];
373     [NSApp sendEvent:[NSApp nextEventMatchingMask:NSEventMaskPressure untilDate:[NSDate dateWithTimeIntervalSinceNow:0.05] inMode:NSDefaultRunLoopMode dequeue:YES]];
374 }
375
376 RetainPtr<NSEvent> EventSenderProxy::beginPressureEvent(int stage)
377 {
378     RetainPtr<EventSenderSyntheticEvent> event = adoptNS([[EventSenderSyntheticEvent alloc] initPressureEventAtLocation:NSMakePoint(m_position.x, m_position.y)
379         globalLocation:([m_testController->mainWebView()->platformWindow() convertRectToScreen:NSMakeRect(m_position.x, m_position.y, 1, 1)].origin)
380         stage:stage
381         pressure:0.5
382         stageTransition:0
383         phase:NSEventPhaseBegan
384         time:absoluteTimeForEventTime(currentEventTime())
385         eventNumber:++eventNumber
386         window:[m_testController->mainWebView()->platformView() window]]);
387
388     return event;
389 }
390
391 RetainPtr<NSEvent> EventSenderProxy::pressureChangeEvent(int stage, float pressure, EventSenderProxy::PressureChangeDirection direction)
392 {
393     RetainPtr<EventSenderSyntheticEvent> event = adoptNS([[EventSenderSyntheticEvent alloc] initPressureEventAtLocation:NSMakePoint(m_position.x, m_position.y)
394         globalLocation:([m_testController->mainWebView()->platformWindow() convertRectToScreen:NSMakeRect(m_position.x, m_position.y, 1, 1)].origin)
395         stage:stage
396         pressure:pressure
397         stageTransition:direction == PressureChangeDirection::Increasing ? 0.5 : -0.5
398         phase:NSEventPhaseChanged
399         time:absoluteTimeForEventTime(currentEventTime())
400         eventNumber:++eventNumber
401         window:[m_testController->mainWebView()->platformView() window]]);
402
403     return event;
404 }
405
406 RetainPtr<NSEvent> EventSenderProxy::pressureChangeEvent(int stage, EventSenderProxy::PressureChangeDirection direction)
407 {
408     return pressureChangeEvent(stage, 0.5, direction);
409 }
410
411 void EventSenderProxy::mouseForceClick()
412 {
413     sendMouseDownToStartPressureEvents();
414
415     RetainPtr<NSEvent> beginPressure = beginPressureEvent(1);
416     RetainPtr<NSEvent> preForceClick = pressureChangeEvent(1, PressureChangeDirection::Increasing);
417     RetainPtr<NSEvent> forceClick = pressureChangeEvent(2, PressureChangeDirection::Increasing);
418     RetainPtr<NSEvent> releasingPressure = pressureChangeEvent(1, PressureChangeDirection::Decreasing);
419     NSEvent *mouseUp = [NSEvent mouseEventWithType:NSEventTypeLeftMouseUp
420         location:NSMakePoint(m_position.x, m_position.y)
421         modifierFlags:0
422         timestamp:absoluteTimeForEventTime(currentEventTime())
423         windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
424         context:[NSGraphicsContext currentContext]
425         eventNumber:++eventNumber
426         clickCount:m_clickCount
427         pressure:0.0];
428
429     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[preForceClick.get() locationInWindow]];
430     targetView = targetView ? targetView : m_testController->mainWebView()->platformView();
431     ASSERT(targetView);
432
433     // Since AppKit does not implement forceup/down as mouse events, we need to send two pressure events to detect
434     // the change in stage that marks those moments.
435     handleForceEventSynchronously(beginPressure.get());
436     handleForceEventSynchronously(preForceClick.get());
437     handleForceEventSynchronously(forceClick.get());
438     handleForceEventSynchronously(releasingPressure.get());
439     [NSApp sendEvent:mouseUp];
440
441     [NSApp _setCurrentEvent:nil];
442 #pragma clang diagnostic push
443 #pragma clang diagnostic ignored "-Wnonnull"
444     // WKView caches the most recent pressure event, so send it a nil event to clear the cache.
445     [targetView pressureChangeWithEvent:nil];
446 #pragma clang diagnostic pop
447 }
448
449 void EventSenderProxy::startAndCancelMouseForceClick()
450 {
451     sendMouseDownToStartPressureEvents();
452
453     RetainPtr<NSEvent> beginPressure = beginPressureEvent(1);
454     RetainPtr<NSEvent> increasingPressure = pressureChangeEvent(1, PressureChangeDirection::Increasing);
455     RetainPtr<NSEvent> releasingPressure = pressureChangeEvent(1, PressureChangeDirection::Decreasing);
456     NSEvent *mouseUp = [NSEvent mouseEventWithType:NSEventTypeLeftMouseUp
457         location:NSMakePoint(m_position.x, m_position.y)
458         modifierFlags:0
459         timestamp:absoluteTimeForEventTime(currentEventTime())
460         windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
461         context:[NSGraphicsContext currentContext]
462         eventNumber:++eventNumber
463         clickCount:m_clickCount
464         pressure:0.0];
465
466     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[beginPressure.get() locationInWindow]];
467     targetView = targetView ? targetView : m_testController->mainWebView()->platformView();
468     ASSERT(targetView);
469
470     // Since AppKit does not implement forceup/down as mouse events, we need to send two pressure events to detect
471     // the change in stage that marks those moments.
472     handleForceEventSynchronously(beginPressure.get());
473     handleForceEventSynchronously(increasingPressure.get());
474     handleForceEventSynchronously(releasingPressure.get());
475     [NSApp sendEvent:mouseUp];
476
477     [NSApp _setCurrentEvent:nil];
478 #pragma clang diagnostic push
479 #pragma clang diagnostic ignored "-Wnonnull"
480     // WKView caches the most recent pressure event, so send it a nil event to clear the cache.
481     [targetView pressureChangeWithEvent:nil];
482 #pragma clang diagnostic pop
483 }
484
485 void EventSenderProxy::mouseForceDown()
486 {
487     sendMouseDownToStartPressureEvents();
488
489     RetainPtr<NSEvent> beginPressure = beginPressureEvent(1);
490     RetainPtr<NSEvent> preForceClick = pressureChangeEvent(1, PressureChangeDirection::Increasing);
491     RetainPtr<NSEvent> forceMouseDown = pressureChangeEvent(2, PressureChangeDirection::Increasing);
492
493     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[beginPressure locationInWindow]];
494     targetView = targetView ? targetView : m_testController->mainWebView()->platformView();
495     ASSERT(targetView);
496
497     // Since AppKit does not implement forceup/down as mouse events, we need to send two pressure events to detect
498     // the change in stage that marks those moments.
499     handleForceEventSynchronously(beginPressure.get());
500     handleForceEventSynchronously(preForceClick.get());
501     [forceMouseDown _postDelayed];
502
503     [NSApp _setCurrentEvent:nil];
504 #pragma clang diagnostic push
505 #pragma clang diagnostic ignored "-Wnonnull"
506     // WKView caches the most recent pressure event, so send it a nil event to clear the cache.
507     [targetView pressureChangeWithEvent:nil];
508 #pragma clang diagnostic pop
509 }
510
511 void EventSenderProxy::mouseForceUp()
512 {
513     RetainPtr<NSEvent> beginPressure = beginPressureEvent(2);
514     RetainPtr<NSEvent> stageTwoEvent = pressureChangeEvent(2, PressureChangeDirection::Decreasing);
515     RetainPtr<NSEvent> stageOneEvent = pressureChangeEvent(1, PressureChangeDirection::Decreasing);
516
517     // Since AppKit does not implement forceup/down as mouse events, we need to send two pressure events to detect
518     // the change in stage that marks those moments.
519     [NSApp sendEvent:beginPressure.get()];
520     [NSApp sendEvent:stageTwoEvent.get()];
521     [NSApp sendEvent:stageOneEvent.get()];
522
523     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[beginPressure locationInWindow]];
524     targetView = targetView ? targetView : m_testController->mainWebView()->platformView();
525     ASSERT(targetView);
526
527     [NSApp _setCurrentEvent:nil];
528
529 #pragma clang diagnostic push
530 #pragma clang diagnostic ignored "-Wnonnull"
531 // WKView caches the most recent pressure event, so send it a nil event to clear the cache.
532     [targetView pressureChangeWithEvent:nil];
533 #pragma clang diagnostic pop
534 }
535
536 void EventSenderProxy::mouseForceChanged(float force)
537 {
538     int stage = force < 1 ? 1 : 2;
539     float pressure = force < 1 ? force : force - 1;
540     RetainPtr<NSEvent> beginPressure = beginPressureEvent(stage);
541     RetainPtr<NSEvent> pressureChangedEvent = pressureChangeEvent(stage, pressure, PressureChangeDirection::Increasing);
542
543     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[beginPressure locationInWindow]];
544     targetView = targetView ? targetView : m_testController->mainWebView()->platformView();
545     ASSERT(targetView);
546
547     [NSApp sendEvent:beginPressure.get()];
548     [NSApp sendEvent:pressureChangedEvent.get()];
549
550 #pragma clang diagnostic push
551 #pragma clang diagnostic ignored "-Wnonnull"
552     // WKView caches the most recent pressure event, so send it a nil event to clear the cache.
553     [targetView pressureChangeWithEvent:nil];
554 #pragma clang diagnostic pop
555 }
556 #else
557
558 #if PLATFORM(COCOA)
559 RetainPtr<NSEvent> EventSenderProxy::beginPressureEvent(int)
560 {
561     return nil;
562 }
563
564 RetainPtr<NSEvent> EventSenderProxy::pressureChangeEvent(int, PressureChangeDirection)
565 {
566     return nil;
567 }
568
569 RetainPtr<NSEvent> EventSenderProxy::pressureChangeEvent(int, float, PressureChangeDirection)
570 {
571     return nil;
572 }
573 #endif // PLATFORM(COCOA)
574
575 void EventSenderProxy::sendMouseDownToStartPressureEvents()
576 {
577 }
578
579 void EventSenderProxy::mouseForceDown()
580 {
581 }
582
583 void EventSenderProxy::mouseForceUp()
584 {
585 }
586
587 void EventSenderProxy::mouseForceChanged(float)
588 {
589 }
590
591 void EventSenderProxy::mouseForceClick()
592 {
593 }
594
595 void EventSenderProxy::startAndCancelMouseForceClick()
596 {
597 }
598 #endif // defined(__LP64__) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101003
599
600 void EventSenderProxy::mouseMoveTo(double x, double y)
601 {
602     NSView *view = m_testController->mainWebView()->platformView();
603     NSPoint position = [view convertPoint:NSMakePoint(x, y) toView:nil];
604     m_position.x = position.x;
605     m_position.y = position.y;
606     NSEvent *event = [NSEvent mouseEventWithType:(m_leftMouseButtonDown ? NSEventTypeLeftMouseDragged : NSEventTypeMouseMoved)
607                                         location:position
608                                    modifierFlags:0 
609                                        timestamp:absoluteTimeForEventTime(currentEventTime())
610                                     windowNumber:view.window.windowNumber
611                                          context:[NSGraphicsContext currentContext] 
612                                      eventNumber:++eventNumber 
613                                       clickCount:(m_leftMouseButtonDown ? m_clickCount : 0) 
614                                         pressure:0];
615
616     NSPoint windowLocation = event.locationInWindow;
617     NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:windowLocation];
618     if (targetView) {
619         [NSApp _setCurrentEvent:event];
620         [targetView mouseMoved:event];
621         [NSApp _setCurrentEvent:nil];
622     } else
623         WTFLogAlways("mouseMoveTo failed to find a target view at %f,%f\n", windowLocation.x, windowLocation.y);
624 }
625
626 void EventSenderProxy::leapForward(int milliseconds)
627 {
628     m_time += milliseconds / 1000.0;
629 }
630
631 void EventSenderProxy::keyDown(WKStringRef key, WKEventModifiers modifiers, unsigned keyLocation)
632 {
633     NSString* character = [NSString stringWithCString:toSTD(key).c_str()
634                                    encoding:[NSString defaultCStringEncoding]];
635
636     NSString *eventCharacter = character;
637     unsigned short keyCode = 0;
638     if ([character isEqualToString:@"leftArrow"]) {
639         const unichar ch = NSLeftArrowFunctionKey;
640         eventCharacter = [NSString stringWithCharacters:&ch length:1];
641         keyCode = 0x7B;
642     } else if ([character isEqualToString:@"rightArrow"]) {
643         const unichar ch = NSRightArrowFunctionKey;
644         eventCharacter = [NSString stringWithCharacters:&ch length:1];
645         keyCode = 0x7C;
646     } else if ([character isEqualToString:@"upArrow"]) {
647         const unichar ch = NSUpArrowFunctionKey;
648         eventCharacter = [NSString stringWithCharacters:&ch length:1];
649         keyCode = 0x7E;
650     } else if ([character isEqualToString:@"downArrow"]) {
651         const unichar ch = NSDownArrowFunctionKey;
652         eventCharacter = [NSString stringWithCharacters:&ch length:1];
653         keyCode = 0x7D;
654     } else if ([character isEqualToString:@"pageUp"]) {
655         const unichar ch = NSPageUpFunctionKey;
656         eventCharacter = [NSString stringWithCharacters:&ch length:1];
657         keyCode = 0x74;
658     } else if ([character isEqualToString:@"pageDown"]) {
659         const unichar ch = NSPageDownFunctionKey;
660         eventCharacter = [NSString stringWithCharacters:&ch length:1];
661         keyCode = 0x79;
662     } else if ([character isEqualToString:@"home"]) {
663         const unichar ch = NSHomeFunctionKey;
664         eventCharacter = [NSString stringWithCharacters:&ch length:1];
665         keyCode = 0x73;
666     } else if ([character isEqualToString:@"end"]) {
667         const unichar ch = NSEndFunctionKey;
668         eventCharacter = [NSString stringWithCharacters:&ch length:1];
669         keyCode = 0x77;
670     } else if ([character isEqualToString:@"insert"]) {
671         const unichar ch = NSInsertFunctionKey;
672         eventCharacter = [NSString stringWithCharacters:&ch length:1];
673         keyCode = 0x72;
674     } else if ([character isEqualToString:@"delete"]) {
675         const unichar ch = NSDeleteFunctionKey;
676         eventCharacter = [NSString stringWithCharacters:&ch length:1];
677         keyCode = 0x75;
678     } else if ([character isEqualToString:@"printScreen"]) {
679         const unichar ch = NSPrintScreenFunctionKey;
680         eventCharacter = [NSString stringWithCharacters:&ch length:1];
681         keyCode = 0x0; // There is no known virtual key code for PrintScreen.
682     } else if ([character isEqualToString:@"cyrillicSmallLetterA"]) {
683         const unichar ch = 0x0430;
684         eventCharacter = [NSString stringWithCharacters:&ch length:1];
685         keyCode = 0x3; // Shares key with "F" on Russian layout.
686     } else if ([character isEqualToString:@"leftControl"]) {
687         const unichar ch = 0xFFE3;
688         eventCharacter = [NSString stringWithCharacters:&ch length:1];
689         keyCode = 0x3B;
690     } else if ([character isEqualToString:@"leftShift"]) {
691         const unichar ch = 0xFFE1;
692         eventCharacter = [NSString stringWithCharacters:&ch length:1];
693         keyCode = 0x38;
694     } else if ([character isEqualToString:@"leftAlt"]) {
695         const unichar ch = 0xFFE7;
696         eventCharacter = [NSString stringWithCharacters:&ch length:1];
697         keyCode = 0x3A;
698     } else if ([character isEqualToString:@"rightControl"]) {
699         const unichar ch = 0xFFE4;
700         eventCharacter = [NSString stringWithCharacters:&ch length:1];
701         keyCode = 0x3E;
702     } else if ([character isEqualToString:@"rightShift"]) {
703         const unichar ch = 0xFFE2;
704         eventCharacter = [NSString stringWithCharacters:&ch length:1];
705         keyCode = 0x3C;
706     } else if ([character isEqualToString:@"rightAlt"]) {
707         const unichar ch = 0xFFE8;
708         eventCharacter = [NSString stringWithCharacters:&ch length:1];
709         keyCode = 0x3D;
710     } else if ([character isEqualToString:@"escape"]) {
711         const unichar ch = 0x1B;
712         eventCharacter = [NSString stringWithCharacters:&ch length:1];
713         keyCode = 0x35;
714     }
715
716     // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
717     // If the input string is a function-key name, set its key code.
718     for (unsigned i = 1; i <= 24; i++) {
719         if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
720             const unichar ch = NSF1FunctionKey + (i - 1);
721             eventCharacter = [NSString stringWithCharacters:&ch length:1];
722             switch (i) {
723                 case 1: keyCode = 0x7A; break;
724                 case 2: keyCode = 0x78; break;
725                 case 3: keyCode = 0x63; break;
726                 case 4: keyCode = 0x76; break;
727                 case 5: keyCode = 0x60; break;
728                 case 6: keyCode = 0x61; break;
729                 case 7: keyCode = 0x62; break;
730                 case 8: keyCode = 0x64; break;
731                 case 9: keyCode = 0x65; break;
732                 case 10: keyCode = 0x6D; break;
733                 case 11: keyCode = 0x67; break;
734                 case 12: keyCode = 0x6F; break;
735                 case 13: keyCode = 0x69; break;
736                 case 14: keyCode = 0x6B; break;
737                 case 15: keyCode = 0x71; break;
738                 case 16: keyCode = 0x6A; break;
739                 case 17: keyCode = 0x40; break;
740                 case 18: keyCode = 0x4F; break;
741                 case 19: keyCode = 0x50; break;
742                 case 20: keyCode = 0x5A; break;
743             }
744         }
745     }
746
747     // FIXME: No keyCode is set for most keys.
748     if ([character isEqualToString:@"\t"])
749         keyCode = 0x30;
750     else if ([character isEqualToString:@" "])
751         keyCode = 0x31;
752     else if ([character isEqualToString:@"\r"])
753         keyCode = 0x24;
754     else if ([character isEqualToString:@"\n"])
755         keyCode = 0x4C;
756     else if ([character isEqualToString:@"\x8"])
757         keyCode = 0x33;
758     else if ([character isEqualToString:@"a"])
759         keyCode = 0x00;
760     else if ([character isEqualToString:@"b"])
761         keyCode = 0x0B;
762     else if ([character isEqualToString:@"d"])
763         keyCode = 0x02;
764     else if ([character isEqualToString:@"e"])
765         keyCode = 0x0E;
766     else if ([character isEqualToString:@"\x1b"])
767         keyCode = 0x1B;
768
769     KeyMappingEntry table[] = {
770         {0x2F, 0x41, '.', nil},
771         {0,    0x43, '*', nil},
772         {0,    0x45, '+', nil},
773         {0,    0x47, NSClearLineFunctionKey, @"clear"},
774         {0x2C, 0x4B, '/', nil},
775         {0,    0x4C, 3, @"enter" },
776         {0x1B, 0x4E, '-', nil},
777         {0x18, 0x51, '=', nil},
778         {0x1D, 0x52, '0', nil},
779         {0x12, 0x53, '1', nil},
780         {0x13, 0x54, '2', nil},
781         {0x14, 0x55, '3', nil},
782         {0x15, 0x56, '4', nil},
783         {0x17, 0x57, '5', nil},
784         {0x16, 0x58, '6', nil},
785         {0x1A, 0x59, '7', nil},
786         {0x1C, 0x5B, '8', nil},
787         {0x19, 0x5C, '9', nil},
788     };
789     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) {
790         NSString* currentCharacterString = [NSString stringWithCharacters:&table[i].character length:1];
791         if ([character isEqualToString:currentCharacterString] || [character isEqualToString:table[i].characterName]) {
792             if (keyLocation == 0x03 /*DOM_KEY_LOCATION_NUMPAD*/)
793                 keyCode = table[i].macNumpadKeyCode;
794             else
795                 keyCode = table[i].macKeyCode;
796             eventCharacter = currentCharacterString;
797             break;
798         }
799     }
800
801     NSString *charactersIgnoringModifiers = eventCharacter;
802
803     int modifierFlags = 0;
804
805     if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
806         modifierFlags |= NSEventModifierFlagShift;
807         charactersIgnoringModifiers = [character lowercaseString];
808     }
809
810     modifierFlags |= buildModifierFlags(modifiers);
811
812     if (keyLocation == 0x03 /*DOM_KEY_LOCATION_NUMPAD*/)
813         modifierFlags |= NSEventModifierFlagNumericPad;
814
815     NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown
816                         location:NSMakePoint(5, 5)
817                         modifierFlags:modifierFlags
818                         timestamp:absoluteTimeForEventTime(currentEventTime())
819                         windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
820                         context:[NSGraphicsContext currentContext]
821                         characters:eventCharacter
822                         charactersIgnoringModifiers:charactersIgnoringModifiers
823                         isARepeat:NO
824                         keyCode:keyCode];
825
826     [NSApp _setCurrentEvent:event];
827     [[m_testController->mainWebView()->platformWindow() firstResponder] keyDown:event];
828     [NSApp _setCurrentEvent:nil];
829
830     event = [NSEvent keyEventWithType:NSEventTypeKeyUp
831                         location:NSMakePoint(5, 5)
832                         modifierFlags:modifierFlags
833                         timestamp:absoluteTimeForEventTime(currentEventTime())
834                         windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
835                         context:[NSGraphicsContext currentContext]
836                         characters:eventCharacter
837                         charactersIgnoringModifiers:charactersIgnoringModifiers
838                         isARepeat:NO
839                         keyCode:keyCode];
840
841     [NSApp _setCurrentEvent:event];
842     [[m_testController->mainWebView()->platformWindow() firstResponder] keyUp:event];
843     [NSApp _setCurrentEvent:nil];
844 }
845
846 void EventSenderProxy::mouseScrollBy(int x, int y)
847 {
848     RetainPtr<CGEventRef> cgScrollEvent = adoptCF(CGEventCreateScrollWheelEvent(0, kCGScrollEventUnitLine, 2, y, x));
849
850     // Set the CGEvent location in flipped coords relative to the first screen, which
851     // compensates for the behavior of +[NSEvent eventWithCGEvent:] when the event has
852     // no associated window. See <rdar://problem/17180591>.
853     CGPoint lastGlobalMousePosition = CGPointMake(m_position.x, [[[NSScreen screens] objectAtIndex:0] frame].size.height - m_position.y);
854     CGEventSetLocation(cgScrollEvent.get(), lastGlobalMousePosition);
855
856     NSEvent *event = [NSEvent eventWithCGEvent:cgScrollEvent.get()];
857     if (NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]]) {
858         [NSApp _setCurrentEvent:event];
859         [targetView scrollWheel:event];
860         [NSApp _setCurrentEvent:nil];
861     } else {
862         NSPoint location = [event locationInWindow];
863         WTFLogAlways("mouseScrollBy failed to find the target view at %f,%f\n", location.x, location.y);
864     }
865 }
866
867 void EventSenderProxy::continuousMouseScrollBy(int x, int y, bool paged)
868 {
869     WTFLogAlways("EventSenderProxy::continuousMouseScrollBy is not implemented\n");
870     return;
871 }
872
873 void EventSenderProxy::mouseScrollByWithWheelAndMomentumPhases(int x, int y, int phase, int momentum)
874 {
875     RetainPtr<CGEventRef> cgScrollEvent = adoptCF(CGEventCreateScrollWheelEvent(0, kCGScrollEventUnitLine, 2, y, x));
876
877     // Set the CGEvent location in flipped coords relative to the first screen, which
878     // compensates for the behavior of +[NSEvent eventWithCGEvent:] when the event has
879     // no associated window. See <rdar://problem/17180591>.
880     CGPoint lastGlobalMousePosition = CGPointMake(m_position.x, [[[NSScreen screens] objectAtIndex:0] frame].size.height - m_position.y);
881     CGEventSetLocation(cgScrollEvent.get(), lastGlobalMousePosition);
882
883     CGEventSetIntegerValueField(cgScrollEvent.get(), kCGScrollWheelEventIsContinuous, 1);
884     CGEventSetIntegerValueField(cgScrollEvent.get(), kCGScrollWheelEventScrollPhase, phase);
885     CGEventSetIntegerValueField(cgScrollEvent.get(), kCGScrollWheelEventMomentumPhase, momentum);
886
887     NSEvent* event = [NSEvent eventWithCGEvent:cgScrollEvent.get()];
888
889     // Our event should have the correct settings:
890     if (NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]]) {
891         [NSApp _setCurrentEvent:event];
892         [targetView scrollWheel:event];
893         [NSApp _setCurrentEvent:nil];
894     } else {
895         NSPoint windowLocation = [event locationInWindow];
896         WTFLogAlways("mouseScrollByWithWheelAndMomentumPhases failed to find the target view at %f,%f\n", windowLocation.x, windowLocation.y);
897     }
898 }
899
900 static NSEventPhase nsEventPhaseFromCGEventPhase(int phase)
901 {
902     switch (phase) {
903     case 0: // kCGSGesturePhaseNone
904         return NSEventPhaseNone;
905     case 1: // kCGSGesturePhaseBegan
906         return NSEventPhaseBegan;
907     case 2: // kCGSGesturePhaseChanged
908         return NSEventPhaseChanged;
909     case 4: // kCGSGesturePhaseEnded
910         return NSEventPhaseEnded;
911     case 8: // kCGSGesturePhaseCancelled
912         return NSEventPhaseCancelled;
913     case 128: // kCGSGesturePhaseMayBegin
914         return NSEventPhaseMayBegin;
915     }
916
917     ASSERT_NOT_REACHED();
918     return NSEventPhaseNone;
919 }
920
921 void EventSenderProxy::swipeGestureWithWheelAndMomentumPhases(int x, int y, int phase, int momentum)
922 {
923     RetainPtr<EventSenderSyntheticEvent> event = adoptNS([[EventSenderSyntheticEvent alloc] init]);
924
925     // "mayBegin" a swipe is actually a scroll wheel event.
926     event->_eventSender_type = (phase == 128) ? NSEventTypeScrollWheel : NSEventTypeGesture;
927     event->_eventSender_subtype = 6; // kIOHIDEventTypeScroll
928     event->_eventSender_locationInWindow = NSMakePoint(m_position.x, m_position.y);
929     event->_eventSender_location = ([m_testController->mainWebView()->platformWindow() convertRectToScreen:NSMakeRect(m_position.x, m_position.y, 1, 1)].origin);
930     event->_eventSender_phase = nsEventPhaseFromCGEventPhase(phase);
931     event->_eventSender_momentumPhase = nsEventPhaseFromCGEventPhase(momentum);
932     event->_eventSender_timestamp = absoluteTimeForEventTime(currentEventTime());
933     event->_eventSender_eventNumber = ++eventNumber;
934
935 #if defined(__LP64__)
936     event->_eventSender_cgsEventRecord.phase = phase;
937     event->_eventSender_cgsEventRecord.deltaX = (float)x;
938     event->_eventSender_cgsEventRecord.deltaY = (float)y;
939 #else
940     NSLog(@"Synthetic swipe gestures are not implemented for 32-bit WebKitTestRunner.");
941 #endif
942
943     [NSApp sendEvent:event.get()];
944 }
945
946 } // namespace WTR