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