PlatformEventFactoryIOS.mm   [plain text]


/*
 * Copyright (C) 2004, 2006-2011, 2014, 2019 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_FAMILY)

#import "IntPoint.h"
#import "KeyEventCocoa.h"
#import "KeyEventCodesIOS.h"
#import "Logging.h"
#import "WAKAppKitStubs.h"
#import "WebEvent.h"
#import "WindowsKeyboardCodes.h"
#import <wtf/Optional.h>
#import <wtf/WallTime.h>

namespace WebCore {

static OptionSet<PlatformEvent::Modifier> modifiersForEvent(WebEvent *event)
{
    OptionSet<PlatformEvent::Modifier> modifiers;

    if (event.modifierFlags & WebEventFlagMaskShiftKey)
        modifiers.add(PlatformEvent::Modifier::ShiftKey);
    if (event.modifierFlags & WebEventFlagMaskControlKey)
        modifiers.add(PlatformEvent::Modifier::ControlKey);
    if (event.modifierFlags & WebEventFlagMaskOptionKey)
        modifiers.add(PlatformEvent::Modifier::AltKey);
    if (event.modifierFlags & WebEventFlagMaskCommandKey)
        modifiers.add(PlatformEvent::Modifier::MetaKey);
    if (event.modifierFlags & WebEventFlagMaskLeftCapsLockKey)
        modifiers.add(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.
        m_modifiers = modifiersForEvent(event);
    }
};

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)
{
    if (event.keyboardFlags & WebEventKeyboardInputModifierFlagsChanged) {
        switch (event.keyCode) {
        case VK_LWIN: // Left Command
        case VK_APPS: // Right Command
            return "Meta"_s;

        case VK_CAPITAL: // Caps Lock
            return "CapsLock"_s;

        case VK_LSHIFT: // Left Shift
        case VK_RSHIFT: // Right Shift
            return "Shift"_s;

        case VK_LMENU: // Left Alt
        case VK_RMENU: // Right Alt
            return "Alt"_s;

        case VK_LCONTROL: // Left Ctrl
        case VK_RCONTROL: // Right Ctrl
            return "Control"_s;

        default:
            ASSERT_NOT_REACHED();
            return emptyString();
        }
    }
    NSString *characters = event.charactersIgnoringModifiers;
    if ([characters length] != 1) {
        LOG(Events, "received an unexpected number of characters in key event: %u", [characters length]);
        return "Unidentified"_s;
    }
    return keyIdentifierForCharCode([characters characterAtIndex:0]);
}

String keyForKeyEvent(WebEvent *event)
{
    if (event.keyboardFlags & WebEventKeyboardInputModifierFlagsChanged) {
        switch (event.keyCode) {
        case VK_LWIN: // Left Command
        case VK_APPS: // Right Command
            return "Meta"_s;

        case VK_CAPITAL: // Caps Lock
            return "CapsLock"_s;

        case VK_LSHIFT: // Left Shift
        case VK_RSHIFT: // Right Shift
            return "Shift"_s;

        case VK_LMENU: // Left Alt
        case VK_RMENU: // Right Alt
            return "Alt"_s;

        case VK_LCONTROL: // Left Ctrl
        case VK_RCONTROL: // Right Ctrl
            return "Control"_s;

        default:
            ASSERT_NOT_REACHED();
            return "Unidentified"_s;
        }
    }

    // If more than one key is being pressed and the key combination includes one or more modifier keys
    // that result in the key no longer producing a printable character (e.g., Control + a), then the
    // key value should be the printable key value that would have been produced if the key had been
    // typed with the default keyboard layout with no modifier keys except for Shift and AltGr applied.
    // See <https://www.w3.org/TR/2015/WD-uievents-20151215/#keys-guidelines>.
    bool isControlDown = event.modifierFlags & WebEventFlagMaskControlKey;
    NSString *characters = isControlDown ? event.charactersIgnoringModifiers : 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 "Dead"_s;
    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 "Backquote"_s;
    case VK_OEM_5: return "Backslash"_s;
    case VK_BACK: return "Backspace"_s;
    case VK_OEM_4: return "BracketLeft"_s;
    case VK_OEM_6: return "BracketRight"_s;
    case VK_OEM_COMMA: return "Comma"_s;
    case VK_0: return "Digit0"_s;
    case VK_1: return "Digit1"_s;
    case VK_2: return "Digit2"_s;
    case VK_3: return "Digit3"_s;
    case VK_4: return "Digit4"_s;
    case VK_5: return "Digit5"_s;
    case VK_6: return "Digit6"_s;
    case VK_7: return "Digit7"_s;
    case VK_8: return "Digit8"_s;
    case VK_9: return "Digit9"_s;
    case VK_OEM_PLUS: return "Equal"_s;
    case VK_OEM_102: return "IntlBackslash"_s;
    // IntlRo.
    // IntlYen.
    case VK_A: return "KeyA"_s;
    case VK_B: return "KeyB"_s;
    case VK_C: return "KeyC"_s;
    case VK_D: return "KeyD"_s;
    case VK_E: return "KeyE"_s;
    case VK_F: return "KeyF"_s;
    case VK_G: return "KeyG"_s;
    case VK_H: return "KeyH"_s;
    case VK_I: return "KeyI"_s;
    case VK_J: return "KeyJ"_s;
    case VK_K: return "KeyK"_s;
    case VK_L: return "KeyL"_s;
    case VK_M: return "KeyM"_s;
    case VK_N: return "KeyN"_s;
    case VK_O: return "KeyO"_s;
    case VK_P: return "KeyP"_s;
    case VK_Q: return "KeyQ"_s;
    case VK_R: return "KeyR"_s;
    case VK_S: return "KeyS"_s;
    case VK_T: return "KeyT"_s;
    case VK_U: return "KeyU"_s;
    case VK_V: return "KeyV"_s;
    case VK_W: return "KeyW"_s;
    case VK_X: return "KeyX"_s;
    case VK_Y: return "KeyY"_s;
    case VK_Z: return "KeyZ"_s;
    case VK_OEM_MINUS: return "Minus"_s;
    case VK_OEM_PERIOD: return "Period"_s;
    case VK_OEM_7: return "Quote"_s;
    case VK_OEM_1: return "Semicolon"_s;
    case VK_OEM_2: return "Slash"_s;

    // Functional keys in alphanumeric section.
    case VK_LMENU: return "AltLeft"_s;
    case VK_RMENU: return "AltRight"_s;
    case VK_CAPITAL: return "CapsLock"_s;
    // ContextMenu.
    case VK_LCONTROL: return "ControlLeft"_s;
    case VK_RCONTROL: return "ControlRight"_s;
    case VK_RETURN: return "Enter"_s; //  Labeled Return on Apple keyboards.
    case VK_LWIN: return "MetaLeft"_s;
    case VK_APPS: return "MetaRight"_s;
    case VK_LSHIFT: return "ShiftLeft"_s;
    case VK_RSHIFT: return "ShiftRight"_s;
    case VK_SPACE: return "Space"_s;
    case VK_TAB: return "Tab"_s;

    // Functional keys found on Japanese and Korean keyboards.
    // Convert.
    case VK_KANA: return "KanaMode"_s;
    // Lang1.
    // Lang2.
    // Lang3.
    // Lang4.
    // Lang5.
    // NonConvert.

    // Keys in the ControlPad section.
    // Delete
    case VK_END: return "End"_s;
    case VK_HELP: return "Help"_s;
    case VK_HOME: return "Home"_s;
    // Insert: Not present on Apple keyboards.
    case VK_NEXT: return "PageDown"_s;
    case VK_PRIOR: return "PageUp"_s;

    // Keys in the ArrowPad section.
    case VK_DOWN: return "ArrowDown"_s;
    case VK_LEFT: return "ArrowLeft"_s;
    case VK_RIGHT: return "ArrowRight"_s;
    case VK_UP: return "ArrowUp"_s;

    // Keys in the Numpad section.
    case VK_NUMLOCK: return "NumLock"_s;
    case VK_NUMPAD0: return "Numpad0"_s;
    case VK_NUMPAD1: return "Numpad1"_s;
    case VK_NUMPAD2: return "Numpad2"_s;
    case VK_NUMPAD3: return "Numpad3"_s;
    case VK_NUMPAD4: return "Numpad4"_s;
    case VK_NUMPAD5: return "Numpad5"_s;
    case VK_NUMPAD6: return "Numpad6"_s;
    case VK_NUMPAD7: return "Numpad7"_s;
    case VK_NUMPAD8: return "Numpad8"_s;
    case VK_NUMPAD9: return "Numpad9"_s;
    case VK_ADD: return "NumpadAdd"_s;
    // NumpadBackspace.
    // NumpadClear.
    // NumpadClearEntry.
    case VK_SEPARATOR: return "NumpadComma"_s; // On JIS keyboard
    case VK_DECIMAL: return "NumpadDecimal"_s;
    case VK_DIVIDE: return "NumpadDivide"_s;
    // NumpadEnter.
    case VK_CLEAR: return "NumpadEqual"_s;
    // NumpadHash.
    // NumpadMemoryAdd.
    // NumpadMemoryClear.
    // NumpadMemoryRecall.
    // NumpadMemoryStore.
    // NumpadMemorySubtract.
    case VK_MULTIPLY: return "NumpadMultiply"_s;
    // NumpadParenLeft.
    // NumpadParenRight.
    // NumpadStar: The specification says to use "NumpadMultiply" for the * key on numeric keypads.
    case VK_SUBTRACT: return "NumpadSubtract"_s;

    // Keys in the Function section.
    case VK_ESCAPE: return "Escape"_s;
    case VK_F1: return "F1"_s;
    case VK_F2: return "F2"_s;
    case VK_F3: return "F3"_s;
    case VK_F4: return "F4"_s;
    case VK_F5: return "F5"_s;
    case VK_F6: return "F6"_s;
    case VK_F7: return "F7"_s;
    case VK_F8: return "F8"_s;
    case VK_F9: return "F9"_s;
    case VK_F10: return "F10"_s;
    case VK_F11: return "F11"_s;
    case VK_F12: return "F12"_s;
    case VK_F13: return "F13"_s;
    case VK_F14: return "F14"_s;
    case VK_F15: return "F15"_s;
    case VK_F16: return "F16"_s;
    case VK_F17: return "F17"_s;
    case VK_F18: return "F18"_s;
    case VK_F19: return "F19"_s;
    case VK_F20: return "F20"_s;
    // 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 "AudioVolumeDown"_s;
    case VK_VOLUME_MUTE: return "AudioVolumeMute"_s;
    case VK_VOLUME_UP: return "AudioVolumeUp"_s;
    // 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 "Unidentified"_s;
    }
}

static bool isKeypadEvent(WebEvent* event)
{
    // Check that this is the type of event that has a keyCode.
    if (event.type != WebEventKeyDown && event.type != WebEventKeyUp)
        return false;

    switch (event.keyCode) {
    case VK_CLEAR: // Num Pad Clear
    case VK_OEM_PLUS: // Num Pad =
    case VK_DIVIDE:
    case VK_MULTIPLY:
    case VK_SUBTRACT:
    case VK_ADD:
    case VK_RETURN: // Num Pad Enter
    case VK_DECIMAL: // Num Pad .
    case VK_SEPARATOR: // Num Pad , (on JIS keyboard)
    case VK_NUMPAD0:
    case VK_NUMPAD1:
    case VK_NUMPAD2:
    case VK_NUMPAD3:
    case VK_NUMPAD4:
    case VK_NUMPAD5:
    case VK_NUMPAD6:
    case VK_NUMPAD7:
    case VK_NUMPAD8:
    case VK_NUMPAD9:
        return true;
    }
    return false;
}

int windowsKeyCodeForKeyEvent(WebEvent* event)
{
    if (event.keyboardFlags & WebEventKeyboardInputModifierFlagsChanged)
        return event.keyCode;

    // There are several kinds of characters for which we produce key code from char code:
    // 1. Roman letters. Windows keyboard layouts affect both virtual key codes and character codes for these,
    //    so e.g. 'A' gets the same keyCode on QWERTY, AZERTY or Dvorak layouts.
    // 2. Keys for which there is no known iOS virtual key codes, like PrintScreen.
    // 3. Certain punctuation keys. On Windows, these are also remapped depending on current keyboard layout,
    //    but see comment in windowsKeyCodeForCharCode().
    if (!isKeypadEvent(event) && (event.type == WebEventKeyDown || event.type == WebEventKeyUp)) {
        // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified characters first.
        NSString *string = event.characters;
        int code = string.length > 0 ? windowsKeyCodeForCharCode([string characterAtIndex:0]) : 0;
        if (code)
            return code;

        // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on -[WebEvent keyCode] below.
        string = event.charactersIgnoringModifiers;
        code = string.length > 0 ? windowsKeyCodeForCharCode([string characterAtIndex:0]) : 0;
        if (code)
            return code;
    }

    // Use iOS virtual key code directly for any keys not handled above.
    // E.g. the key next to Caps Lock has the same Event.keyCode on U.S. keyboard ('A') and on
    // Russian keyboard (CYRILLIC LETTER EF).
    return event.keyCode;
}

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();

        if (event.keyboardFlags & WebEventKeyboardInputModifierFlagsChanged) {
            m_text = emptyString();
            m_unmodifiedText = emptyString();
            m_autoRepeat = false;
        } else {
            m_text = event.characters;
            m_unmodifiedText = event.charactersIgnoringModifiers;
            m_autoRepeat = event.isKeyRepeating;
        }
        m_key = keyForKeyEvent(event);
        m_code = codeForKeyEvent(event);
        m_keyIdentifier = keyIdentifierForKeyEvent(event);
        m_windowsVirtualKeyCode = windowsKeyCodeForKeyEvent(event);
        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_FAMILY)