blob: ece5b523e293fbd1af8a313fa24245db10ba4e04 [file] [log] [blame]
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
* Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "EventSenderProxy.h"
#import "PlatformWebView.h"
#import "StringFunctions.h"
#import "TestController.h"
#import <wtf/RetainPtr.h>
#import <WebKit2/WKString.h>
namespace WTR {
enum MouseAction {
MouseDown,
MouseUp,
MouseDragged
};
// Match the DOM spec (sadly the DOM spec does not provide an enum)
enum MouseButton {
LeftMouseButton = 0,
MiddleMouseButton = 1,
RightMouseButton = 2,
NoMouseButton = -1
};
static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action)
{
switch (button) {
case LeftMouseButton:
switch (action) {
case MouseDown:
return NSLeftMouseDown;
case MouseUp:
return NSLeftMouseUp;
case MouseDragged:
return NSLeftMouseDragged;
}
case RightMouseButton:
switch (action) {
case MouseDown:
return NSRightMouseDown;
case MouseUp:
return NSRightMouseUp;
case MouseDragged:
return NSRightMouseDragged;
}
default:
switch (action) {
case MouseDown:
return NSOtherMouseDown;
case MouseUp:
return NSOtherMouseUp;
case MouseDragged:
return NSOtherMouseDragged;
}
}
assert(0);
return static_cast<NSEventType>(0);
}
static int buildModifierFlags(WKEventModifiers modifiers)
{
int flags = 0;
if (modifiers & kWKEventModifiersControlKey)
flags |= NSControlKeyMask;
if (modifiers & kWKEventModifiersShiftKey)
flags |= NSShiftKeyMask;
if (modifiers & kWKEventModifiersAltKey)
flags |= NSAlternateKeyMask;
if (modifiers & kWKEventModifiersMetaKey)
flags |= NSCommandKeyMask;
return flags;
}
EventSenderProxy::EventSenderProxy(TestController* testController)
: m_testController(testController)
, m_time(0)
, m_position()
, m_leftMouseButtonDown(false)
, m_clickCount(0)
, m_clickTime(0)
, m_clickPosition()
, m_clickButton(kWKEventMouseButtonNoButton)
, eventNumber(0)
{
}
EventSenderProxy::~EventSenderProxy()
{
}
void EventSenderProxy::updateClickCountForButton(int button)
{
if (m_time - m_clickTime < 1 && m_position == m_clickPosition && button == m_clickButton) {
++m_clickCount;
m_clickTime = m_time;
return;
}
m_clickCount = 1;
m_clickTime = m_time;
m_clickPosition = m_position;
m_clickButton = button;
}
void EventSenderProxy::mouseDown(unsigned buttonNumber, WKEventModifiers modifiers)
{
updateClickCountForButton(buttonNumber);
NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
NSEvent *event = [NSEvent mouseEventWithType:eventType
location:NSMakePoint(m_position.x, m_position.y)
modifierFlags:buildModifierFlags(modifiers)
timestamp:currentEventTime()
windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
context:[NSGraphicsContext currentContext]
eventNumber:++eventNumber
clickCount:m_clickCount
pressure:0.0];
NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]];
if (targetView) {
[targetView mouseDown:event];
if (buttonNumber == LeftMouseButton)
m_leftMouseButtonDown = true;
}
}
void EventSenderProxy::mouseUp(unsigned buttonNumber, WKEventModifiers modifiers)
{
NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
NSEvent *event = [NSEvent mouseEventWithType:eventType
location:NSMakePoint(m_position.x, m_position.y)
modifierFlags:buildModifierFlags(modifiers)
timestamp:currentEventTime()
windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
context:[NSGraphicsContext currentContext]
eventNumber:++eventNumber
clickCount:m_clickCount
pressure:0.0];
NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]];
targetView = targetView ? targetView : m_testController->mainWebView()->platformView();
assert(targetView);
[targetView mouseUp:event];
if (buttonNumber == LeftMouseButton)
m_leftMouseButtonDown = false;
m_clickTime = [event timestamp];
m_clickPosition = m_position;
}
void EventSenderProxy::mouseMoveTo(double x, double y)
{
NSView *view = m_testController->mainWebView()->platformView();
NSPoint position = [view convertPoint:NSMakePoint(x, y) toView:nil];
m_position.x = position.x;
m_position.y = position.y;
NSEvent *event = [NSEvent mouseEventWithType:(m_leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved)
location:position
modifierFlags:0
timestamp:currentEventTime()
windowNumber:[[view window] windowNumber]
context:[NSGraphicsContext currentContext]
eventNumber:++eventNumber
clickCount:(m_leftMouseButtonDown ? m_clickCount : 0)
pressure:0.0];
NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]];
if (targetView)
[targetView mouseMoved:event];
}
void EventSenderProxy::leapForward(int milliseconds)
{
m_time += milliseconds / 1000.0;
}
void EventSenderProxy::keyDown(WKStringRef key, WKEventModifiers modifiers, unsigned keyLocation)
{
NSString* character = [NSString stringWithCString:toSTD(key).c_str()
encoding:[NSString defaultCStringEncoding]];
NSString *eventCharacter = character;
unsigned short keyCode = 0;
if ([character isEqualToString:@"leftArrow"]) {
const unichar ch = NSLeftArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x7B;
} else if ([character isEqualToString:@"rightArrow"]) {
const unichar ch = NSRightArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x7C;
} else if ([character isEqualToString:@"upArrow"]) {
const unichar ch = NSUpArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x7E;
} else if ([character isEqualToString:@"downArrow"]) {
const unichar ch = NSDownArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x7D;
} else if ([character isEqualToString:@"pageUp"]) {
const unichar ch = NSPageUpFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x74;
} else if ([character isEqualToString:@"pageDown"]) {
const unichar ch = NSPageDownFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x79;
} else if ([character isEqualToString:@"home"]) {
const unichar ch = NSHomeFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x73;
} else if ([character isEqualToString:@"end"]) {
const unichar ch = NSEndFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x77;
} else if ([character isEqualToString:@"insert"]) {
const unichar ch = NSInsertFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x72;
} else if ([character isEqualToString:@"delete"]) {
const unichar ch = NSDeleteFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x75;
} else if ([character isEqualToString:@"printScreen"]) {
const unichar ch = NSPrintScreenFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x0; // There is no known virtual key code for PrintScreen.
} else if ([character isEqualToString:@"cyrillicSmallLetterA"]) {
const unichar ch = 0x0430;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x3; // Shares key with "F" on Russian layout.
} else if ([character isEqualToString:@"leftControl"]) {
const unichar ch = 0xFFE3;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x3B;
} else if ([character isEqualToString:@"leftShift"]) {
const unichar ch = 0xFFE1;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x38;
} else if ([character isEqualToString:@"leftAlt"]) {
const unichar ch = 0xFFE7;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x3A;
} else if ([character isEqualToString:@"rightControl"]) {
const unichar ch = 0xFFE4;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x3E;
} else if ([character isEqualToString:@"rightShift"]) {
const unichar ch = 0xFFE2;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x3C;
} else if ([character isEqualToString:@"rightAlt"]) {
const unichar ch = 0xFFE8;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
keyCode = 0x3D;
}
// Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
// If the input string is a function-key name, set its key code.
for (unsigned i = 1; i <= 24; i++) {
if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
const unichar ch = NSF1FunctionKey + (i - 1);
eventCharacter = [NSString stringWithCharacters:&ch length:1];
switch (i) {
case 1: keyCode = 0x7A; break;
case 2: keyCode = 0x78; break;
case 3: keyCode = 0x63; break;
case 4: keyCode = 0x76; break;
case 5: keyCode = 0x60; break;
case 6: keyCode = 0x61; break;
case 7: keyCode = 0x62; break;
case 8: keyCode = 0x64; break;
case 9: keyCode = 0x65; break;
case 10: keyCode = 0x6D; break;
case 11: keyCode = 0x67; break;
case 12: keyCode = 0x6F; break;
case 13: keyCode = 0x69; break;
case 14: keyCode = 0x6B; break;
case 15: keyCode = 0x71; break;
case 16: keyCode = 0x6A; break;
case 17: keyCode = 0x40; break;
case 18: keyCode = 0x4F; break;
case 19: keyCode = 0x50; break;
case 20: keyCode = 0x5A; break;
}
}
}
// FIXME: No keyCode is set for most keys.
if ([character isEqualToString:@"\t"])
keyCode = 0x30;
else if ([character isEqualToString:@" "])
keyCode = 0x31;
else if ([character isEqualToString:@"\r"])
keyCode = 0x24;
else if ([character isEqualToString:@"\n"])
keyCode = 0x4C;
else if ([character isEqualToString:@"\x8"])
keyCode = 0x33;
else if ([character isEqualToString:@"7"])
keyCode = 0x1A;
else if ([character isEqualToString:@"5"])
keyCode = 0x17;
else if ([character isEqualToString:@"9"])
keyCode = 0x19;
else if ([character isEqualToString:@"0"])
keyCode = 0x1D;
else if ([character isEqualToString:@"a"])
keyCode = 0x00;
else if ([character isEqualToString:@"b"])
keyCode = 0x0B;
else if ([character isEqualToString:@"d"])
keyCode = 0x02;
else if ([character isEqualToString:@"e"])
keyCode = 0x0E;
NSString *charactersIgnoringModifiers = eventCharacter;
int modifierFlags = 0;
if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
modifierFlags |= NSShiftKeyMask;
charactersIgnoringModifiers = [character lowercaseString];
}
modifierFlags |= buildModifierFlags(modifiers);
if (keyLocation == 0x03 /*DOM_KEY_LOCATION_NUMPAD*/)
modifierFlags |= NSNumericPadKeyMask;
// FIXME: [[[mainFrame frameView] documentView] layout];
NSEvent *event = [NSEvent keyEventWithType:NSKeyDown
location:NSMakePoint(5, 5)
modifierFlags:modifierFlags
timestamp:currentEventTime()
windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
context:[NSGraphicsContext currentContext]
characters:eventCharacter
charactersIgnoringModifiers:charactersIgnoringModifiers
isARepeat:NO
keyCode:keyCode];
[[m_testController->mainWebView()->platformWindow() firstResponder] keyDown:event];
event = [NSEvent keyEventWithType:NSKeyUp
location:NSMakePoint(5, 5)
modifierFlags:modifierFlags
timestamp:currentEventTime()
windowNumber:[m_testController->mainWebView()->platformWindow() windowNumber]
context:[NSGraphicsContext currentContext]
characters:eventCharacter
charactersIgnoringModifiers:charactersIgnoringModifiers
isARepeat:NO
keyCode:keyCode];
[[m_testController->mainWebView()->platformWindow() firstResponder] keyUp:event];
}
void EventSenderProxy::mouseScrollBy(int x, int y)
{
RetainPtr<CGEventRef> cgScrollEvent(AdoptCF, CGEventCreateScrollWheelEvent(0, kCGScrollEventUnitLine, 2, y, x));
// CGEvent locations are in global display coordinates.
CGPoint lastGlobalMousePosition = CGPointMake(m_position.x, [[NSScreen mainScreen] frame].size.height - m_position.y);
CGEventSetLocation(cgScrollEvent.get(), lastGlobalMousePosition);
NSEvent *event = [NSEvent eventWithCGEvent:cgScrollEvent.get()];
if (NSView *targetView = [m_testController->mainWebView()->platformView() hitTest:[event locationInWindow]])
[targetView scrollWheel:event];
}
void EventSenderProxy::continuousMouseScrollBy(int x, int y, bool paged)
{
// FIXME: Implement this.
return;
}
} // namespace WTR