blob: c97459b0d8316343c7116f98a08e15dc9359a778 [file] [log] [blame]
/*
* Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2014 Apple Inc. All rights reserved.
*
* 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 "PlatformEventFactoryIOS.h"
#if PLATFORM(IOS)
#import "IntPoint.h"
#import "KeyEventCocoa.h"
#import "Logging.h"
#import "WAKAppKitStubs.h"
#import "WebEvent.h"
#import "WindowsKeyboardCodes.h"
#import <wtf/WallTime.h>
namespace WebCore {
static OptionSet<PlatformEvent::Modifier> modifiersForEvent(WebEvent *event)
{
OptionSet<PlatformEvent::Modifier> modifiers;
if (event.modifierFlags & WebEventFlagMaskShift)
modifiers |= PlatformEvent::Modifier::ShiftKey;
if (event.modifierFlags & WebEventFlagMaskControl)
modifiers |= PlatformEvent::Modifier::CtrlKey;
if (event.modifierFlags & WebEventFlagMaskAlternate)
modifiers |= PlatformEvent::Modifier::AltKey;
if (event.modifierFlags & WebEventFlagMaskCommand)
modifiers |= PlatformEvent::Modifier::MetaKey;
if (event.modifierFlags & WebEventFlagMaskAlphaShift)
modifiers |= PlatformEvent::Modifier::CapsLockKey;
return modifiers;
}
static inline IntPoint pointForEvent(WebEvent *event)
{
return IntPoint(event.locationInWindow);
}
static inline IntPoint globalPointForEvent(WebEvent *event)
{
// iOS WebKit works as if it is full screen. Therefore Web coords are Global coords.
return pointForEvent(event);
}
static PlatformEvent::Type mouseEventType(WebEvent *event)
{
switch (event.type) {
case WebEventMouseDown:
return PlatformEvent::MousePressed;
case WebEventMouseUp:
return PlatformEvent::MouseReleased;
case WebEventMouseMoved:
return PlatformEvent::MouseMoved;
default:
ASSERT_NOT_REACHED();
return PlatformEvent::MousePressed;
}
}
class PlatformMouseEventBuilder : public PlatformMouseEvent {
public:
PlatformMouseEventBuilder(WebEvent *event)
{
m_type = mouseEventType(event);
m_timestamp = WallTime::now();
m_position = pointForEvent(event);
m_globalPosition = globalPointForEvent(event);
m_button = LeftButton; // This has always been the LeftButton on iOS.
m_clickCount = 1; // This has always been 1 on iOS.
}
};
PlatformMouseEvent PlatformEventFactory::createPlatformMouseEvent(WebEvent *event)
{
return PlatformMouseEventBuilder(event);
}
class PlatformWheelEventBuilder : public PlatformWheelEvent {
public:
PlatformWheelEventBuilder(WebEvent *event)
{
ASSERT(event.type == WebEventScrollWheel);
m_type = PlatformEvent::Wheel;
m_timestamp = WallTime::now();
m_position = pointForEvent(event);
m_globalPosition = globalPointForEvent(event);
m_deltaX = event.deltaX;
m_deltaY = event.deltaY;
m_granularity = ScrollByPixelWheelEvent; // iOS only supports continuous (pixel-mode) scrolling.
}
};
PlatformWheelEvent PlatformEventFactory::createPlatformWheelEvent(WebEvent *event)
{
return PlatformWheelEventBuilder(event);
}
String keyIdentifierForKeyEvent(WebEvent *event)
{
NSString *s = event.charactersIgnoringModifiers;
if ([s length] != 1) {
LOG(Events, "received an unexpected number of characters in key event: %u", [s length]);
return "Unidentified";
}
return keyIdentifierForCharCode(CFStringGetCharacterAtIndex((CFStringRef)s, 0));
}
String keyForKeyEvent(WebEvent *event)
{
NSString *characters = event.characters;
auto length = [characters length];
// characters return an empty string for dead keys.
// https://developer.apple.com/reference/appkit/nsevent/1534183-characters
// "Dead" is defined here https://w3c.github.io/uievents-key/#keys-composition.
if (!length)
return ASCIILiteral("Dead");
if (length > 1)
return characters;
return keyForCharCode([characters characterAtIndex:0]);
}
// https://w3c.github.io/uievents-code/
String codeForKeyEvent(WebEvent *event)
{
switch (event.keyCode) {
// Keys in the alphanumeric section.
case VK_OEM_3: return ASCIILiteral("Backquote");
case VK_OEM_5: return ASCIILiteral("Backslash");
case VK_BACK: return ASCIILiteral("Backspace");
case VK_OEM_4: return ASCIILiteral("BracketLeft");
case VK_OEM_6: return ASCIILiteral("BracketRight");
case VK_OEM_COMMA: return ASCIILiteral("Comma");
case VK_0: return ASCIILiteral("Digit0");
case VK_1: return ASCIILiteral("Digit1");
case VK_2: return ASCIILiteral("Digit2");
case VK_3: return ASCIILiteral("Digit3");
case VK_4: return ASCIILiteral("Digit4");
case VK_5: return ASCIILiteral("Digit5");
case VK_6: return ASCIILiteral("Digit6");
case VK_7: return ASCIILiteral("Digit7");
case VK_8: return ASCIILiteral("Digit8");
case VK_9: return ASCIILiteral("Digit9");
case VK_OEM_PLUS: return ASCIILiteral("Equal");
case VK_OEM_102: return ASCIILiteral("IntlBackslash");
// IntlRo.
// IntlYen.
case VK_A: return ASCIILiteral("KeyA");
case VK_B: return ASCIILiteral("KeyB");
case VK_C: return ASCIILiteral("KeyC");
case VK_D: return ASCIILiteral("KeyD");
case VK_E: return ASCIILiteral("KeyE");
case VK_F: return ASCIILiteral("KeyF");
case VK_G: return ASCIILiteral("KeyG");
case VK_H: return ASCIILiteral("KeyH");
case VK_I: return ASCIILiteral("KeyI");
case VK_J: return ASCIILiteral("KeyJ");
case VK_K: return ASCIILiteral("KeyK");
case VK_L: return ASCIILiteral("KeyL");
case VK_M: return ASCIILiteral("KeyM");
case VK_N: return ASCIILiteral("KeyN");
case VK_O: return ASCIILiteral("KeyO");
case VK_P: return ASCIILiteral("KeyP");
case VK_Q: return ASCIILiteral("KeyQ");
case VK_R: return ASCIILiteral("KeyR");
case VK_S: return ASCIILiteral("KeyS");
case VK_T: return ASCIILiteral("KeyT");
case VK_U: return ASCIILiteral("KeyU");
case VK_V: return ASCIILiteral("KeyV");
case VK_W: return ASCIILiteral("KeyW");
case VK_X: return ASCIILiteral("KeyX");
case VK_Y: return ASCIILiteral("KeyY");
case VK_Z: return ASCIILiteral("KeyZ");
case VK_OEM_MINUS: return ASCIILiteral("Minus");
case VK_OEM_PERIOD: return ASCIILiteral("Period");
case VK_OEM_7: return ASCIILiteral("Quote");
case VK_OEM_1: return ASCIILiteral("Semicolon");
case VK_OEM_2: return ASCIILiteral("Slash");
// Functional keys in alphanumeric section.
case VK_MENU: return ASCIILiteral("AltLeft");
// AltRight.
case VK_CAPITAL: return ASCIILiteral("CapsLock");
// ContextMenu.
case VK_LCONTROL: return ASCIILiteral("ControlLeft");
case VK_RCONTROL: return ASCIILiteral("ControlRight");
case VK_RETURN: return ASCIILiteral("Enter"); // Labeled Return on Apple keyboards.
case VK_LWIN: return ASCIILiteral("MetaLeft");
case VK_RWIN: return ASCIILiteral("MetaRight");
case VK_LSHIFT: return ASCIILiteral("ShiftLeft");
case VK_RSHIFT: return ASCIILiteral("ShiftRight");
case VK_SPACE: return ASCIILiteral("Space");
case VK_TAB: return ASCIILiteral("Tab");
// Functional keys found on Japanese and Korean keyboards.
// Convert.
case VK_KANA: return ASCIILiteral("KanaMode");
// Lang1.
// Lang2.
// Lang3.
// Lang4.
// Lang5.
// NonConvert.
// Keys in the ControlPad section.
// Delete
case VK_END: return ASCIILiteral("End");
case VK_HELP: return ASCIILiteral("Help");
case VK_HOME: return ASCIILiteral("Home");
// Insert: Not present on Apple keyboards.
case VK_NEXT: return ASCIILiteral("PageDown");
case VK_PRIOR: return ASCIILiteral("PageUp");
// Keys in the ArrowPad section.
case VK_DOWN: return ASCIILiteral("ArrowDown");
case VK_LEFT: return ASCIILiteral("ArrowLeft");
case VK_RIGHT: return ASCIILiteral("ArrowRight");
case VK_UP: return ASCIILiteral("ArrowUp");
// Keys in the Numpad section.
case VK_NUMLOCK: return ASCIILiteral("NumLock");
case VK_NUMPAD0: return ASCIILiteral("Numpad0");
case VK_NUMPAD1: return ASCIILiteral("Numpad1");
case VK_NUMPAD2: return ASCIILiteral("Numpad2");
case VK_NUMPAD3: return ASCIILiteral("Numpad3");
case VK_NUMPAD4: return ASCIILiteral("Numpad4");
case VK_NUMPAD5: return ASCIILiteral("Numpad5");
case VK_NUMPAD6: return ASCIILiteral("Numpad6");
case VK_NUMPAD7: return ASCIILiteral("Numpad7");
case VK_NUMPAD8: return ASCIILiteral("Numpad8");
case VK_NUMPAD9: return ASCIILiteral("Numpad9");
case VK_ADD: return ASCIILiteral("NumpadAdd");
// NumpadBackspace.
// NumpadClear.
// NumpadClearEntry.
case VK_SEPARATOR: return ASCIILiteral("NumpadComma");
case VK_DECIMAL: return ASCIILiteral("NumpadDecimal");
case VK_DIVIDE: return ASCIILiteral("NumpadDivide");
// NumpadEnter.
case VK_CLEAR: return ASCIILiteral("NumpadEqual");
// NumpadHash.
// NumpadMemoryAdd.
// NumpadMemoryClear.
// NumpadMemoryRecall.
// NumpadMemoryStore.
// NumpadMemorySubtract.
case VK_MULTIPLY: return ASCIILiteral("NumpadMultiply");
// NumpadParenLeft.
// NumpadParenRight.
// NumpadStar: The specification says to use "NumpadMultiply" for the * key on numeric keypads.
case VK_SUBTRACT: return ASCIILiteral("NumpadSubtract");
// Keys in the Function section.
case VK_ESCAPE: return ASCIILiteral("Escape");
case VK_F1: return ASCIILiteral("F1");
case VK_F2: return ASCIILiteral("F2");
case VK_F3: return ASCIILiteral("F3");
case VK_F4: return ASCIILiteral("F4");
case VK_F5: return ASCIILiteral("F5");
case VK_F6: return ASCIILiteral("F6");
case VK_F7: return ASCIILiteral("F7");
case VK_F8: return ASCIILiteral("F8");
case VK_F9: return ASCIILiteral("F9");
case VK_F10: return ASCIILiteral("F10");
case VK_F11: return ASCIILiteral("F11");
case VK_F12: return ASCIILiteral("F12");
case VK_F13: return ASCIILiteral("F13");
case VK_F14: return ASCIILiteral("F14");
case VK_F15: return ASCIILiteral("F15");
case VK_F16: return ASCIILiteral("F16");
case VK_F17: return ASCIILiteral("F17");
case VK_F18: return ASCIILiteral("F18");
case VK_F19: return ASCIILiteral("F19");
case VK_F20: return ASCIILiteral("F20");
// Fn: This is typically a hardware key that does not generate a separate code.
// FnLock.
// PrintScreen.
// ScrollLock.
// Pause.
// Media keys.
// BrowserBack.
// BrowserFavorites.
// BrowserForward.
// BrowserHome.
// BrowserRefresh.
// BrowserSearch.
// BrowserStop.
// Eject.
// LaunchApp1.
// LaunchApp2.
// LaunchMail.
// MediaPlayPause.
// MediaSelect.
// MediaStop.
// MediaTrackNext.
// MediaTrackPrevious.
// Power.
// Sleep.
case VK_VOLUME_DOWN: return ASCIILiteral("AudioVolumeDown");
case VK_VOLUME_MUTE: return ASCIILiteral("AudioVolumeMute");
case VK_VOLUME_UP: return ASCIILiteral("AudioVolumeUp");
// WakeUp.
// Legacy modifier keys.
// Hyper.
// Super.
// Turbo.
// Legacy process control keys.
// Abort.
// Resume.
// Suspend.
// Legacy editing keys.
// Again.
// Copy.
// Cut.
// Find.
// Open.
// Paste.
// Props.
// Select.
// Undo.
// Keys found on international keyboards.
// Hiragana.
// Katakana.
default:
return ASCIILiteral("Unidentified");
}
}
class PlatformKeyboardEventBuilder : public PlatformKeyboardEvent {
public:
PlatformKeyboardEventBuilder(WebEvent *event)
{
ASSERT(event.type == WebEventKeyDown || event.type == WebEventKeyUp);
m_type = (event.type == WebEventKeyUp ? PlatformEvent::KeyUp : PlatformEvent::KeyDown);
m_modifiers = modifiersForEvent(event);
m_timestamp = WallTime::now();
m_text = event.characters;
m_unmodifiedText = event.charactersIgnoringModifiers;
m_key = keyForKeyEvent(event);
m_code = codeForKeyEvent(event);
m_keyIdentifier = keyIdentifierForKeyEvent(event);
m_windowsVirtualKeyCode = event.keyCode;
m_autoRepeat = event.isKeyRepeating;
m_isKeypad = false; // iOS does not distinguish the numpad. See <rdar://problem/7190835>.
m_isSystemKey = false;
m_Event = event;
// Always use 13 for Enter/Return -- we don't want to use AppKit's different character for Enter.
if (m_windowsVirtualKeyCode == '\r') {
m_text = "\r";
m_unmodifiedText = "\r";
}
// The adjustments below are only needed in backward compatibility mode, but we cannot tell what mode we are in from here.
// Turn 0x7F into 8, because backspace needs to always be 8.
if (m_text == "\x7F")
m_text = "\x8";
if (m_unmodifiedText == "\x7F")
m_unmodifiedText = "\x8";
// Always use 9 for tab -- we don't want to use AppKit's different character for shift-tab.
if (m_windowsVirtualKeyCode == 9) {
m_text = "\x9";
m_unmodifiedText = "\x9";
}
}
};
PlatformKeyboardEvent PlatformEventFactory::createPlatformKeyboardEvent(WebEvent *event)
{
return PlatformKeyboardEventBuilder(event);
}
#if ENABLE(TOUCH_EVENTS)
static PlatformTouchPoint::TouchPhaseType convertTouchPhase(NSNumber *touchPhaseNumber)
{
WebEventTouchPhaseType touchPhase = static_cast<WebEventTouchPhaseType>([touchPhaseNumber unsignedIntValue]);
switch (touchPhase) {
case WebEventTouchPhaseBegan:
return PlatformTouchPoint::TouchPhaseBegan;
case WebEventTouchPhaseMoved:
return PlatformTouchPoint::TouchPhaseMoved;
case WebEventTouchPhaseStationary:
return PlatformTouchPoint::TouchPhaseStationary;
case WebEventTouchPhaseEnded:
return PlatformTouchPoint::TouchPhaseEnded;
case WebEventTouchPhaseCancelled:
return PlatformTouchPoint::TouchPhaseCancelled;
default:
ASSERT_NOT_REACHED();
}
return PlatformTouchPoint::TouchPhaseBegan;
}
static PlatformEvent::Type touchEventType(WebEvent *event)
{
switch (event.type) {
case WebEventTouchBegin:
return PlatformEvent::TouchStart;
case WebEventTouchEnd:
return PlatformEvent::TouchEnd;
case WebEventTouchCancel:
return PlatformEvent::TouchCancel;
case WebEventTouchChange:
return PlatformEvent::TouchMove;
default:
ASSERT_NOT_REACHED();
return PlatformEvent::TouchCancel;
}
}
static PlatformTouchPoint::TouchPhaseType touchPhaseFromPlatformEventType(PlatformEvent::Type type)
{
switch (type) {
case PlatformEvent::TouchStart:
return PlatformTouchPoint::TouchPhaseBegan;
case PlatformEvent::TouchMove:
return PlatformTouchPoint::TouchPhaseMoved;
case PlatformEvent::TouchEnd:
return PlatformTouchPoint::TouchPhaseEnded;
default:
ASSERT_NOT_REACHED();
return PlatformTouchPoint::TouchPhaseCancelled;
}
}
class PlatformTouchPointBuilder : public PlatformTouchPoint {
public:
PlatformTouchPointBuilder(unsigned identifier, const IntPoint& location, TouchPhaseType phase)
: PlatformTouchPoint(identifier, location, phase)
{
}
};
class PlatformTouchEventBuilder : public PlatformTouchEvent {
public:
PlatformTouchEventBuilder(WebEvent *event)
{
m_type = touchEventType(event);
m_modifiers = modifiersForEvent(event);
m_timestamp = WallTime::fromRawSeconds(event.timestamp);
m_gestureScale = event.gestureScale;
m_gestureRotation = event.gestureRotation;
m_isGesture = event.isGesture;
m_position = pointForEvent(event);
m_globalPosition = globalPointForEvent(event);
unsigned touchCount = event.touchCount;
m_touchPoints.reserveInitialCapacity(touchCount);
for (unsigned i = 0; i < touchCount; ++i) {
unsigned identifier = [(NSNumber *)[event.touchIdentifiers objectAtIndex:i] unsignedIntValue];
IntPoint location = IntPoint([(NSValue *)[event.touchLocations objectAtIndex:i] pointValue]);
PlatformTouchPoint::TouchPhaseType touchPhase = convertTouchPhase([event.touchPhases objectAtIndex:i]);
m_touchPoints.uncheckedAppend(PlatformTouchPointBuilder(identifier, location, touchPhase));
}
}
PlatformTouchEventBuilder(PlatformEvent::Type type, IntPoint location)
{
m_type = type;
m_timestamp = WallTime::now();
m_gestureScale = 1;
m_gestureRotation = 0;
m_isGesture = 0;
m_position = location;
m_globalPosition = location;
m_isPotentialTap = true;
unsigned touchCount = 1;
m_touchPoints.reserveInitialCapacity(touchCount);
for (unsigned i = 0; i < touchCount; ++i)
m_touchPoints.uncheckedAppend(PlatformTouchPointBuilder(1, location, touchPhaseFromPlatformEventType(type)));
}
};
PlatformTouchEvent PlatformEventFactory::createPlatformTouchEvent(WebEvent *event)
{
return PlatformTouchEventBuilder(event);
}
PlatformTouchEvent PlatformEventFactory::createPlatformSimulatedTouchEvent(PlatformEvent::Type type, IntPoint location)
{
return PlatformTouchEventBuilder(type, location);
}
#endif // ENABLE(TOUCH_EVENTS)
} // namespace WebCore
#endif // PLATFORM(IOS)