[iOS] Arrow keys do not dispatch DOM events to non-editable elements
[WebKit-https.git] / Source / WebCore / platform / ios / PlatformEventFactoryIOS.mm
1 /*
2  * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "PlatformEventFactoryIOS.h"
28
29 #if PLATFORM(IOS)
30
31 #import "IntPoint.h"
32 #import "KeyEventCocoa.h"
33 #import "KeyEventCodesIOS.h"
34 #import "Logging.h"
35 #import "WAKAppKitStubs.h"
36 #import "WebEvent.h"
37 #import "WindowsKeyboardCodes.h"
38 #import <UIKit/UIKit.h>
39 #import <wtf/Optional.h>
40 #import <wtf/SoftLinking.h>
41 #import <wtf/WallTime.h>
42
43 SOFT_LINK_FRAMEWORK(UIKit)
44 SOFT_LINK_CONSTANT(UIKit, UIKeyInputUpArrow, NSString *)
45 SOFT_LINK_CONSTANT(UIKit, UIKeyInputDownArrow, NSString *)
46 SOFT_LINK_CONSTANT(UIKit, UIKeyInputLeftArrow, NSString *)
47 SOFT_LINK_CONSTANT(UIKit, UIKeyInputRightArrow, NSString *)
48
49 #define UIKeyInputUpArrow getUIKeyInputUpArrow()
50 #define UIKeyInputDownArrow getUIKeyInputDownArrow()
51 #define UIKeyInputLeftArrow getUIKeyInputLeftArrow()
52 #define UIKeyInputRightArrow getUIKeyInputRightArrow()
53
54 namespace WebCore {
55
56 static OptionSet<PlatformEvent::Modifier> modifiersForEvent(WebEvent *event)
57 {
58     OptionSet<PlatformEvent::Modifier> modifiers;
59
60     if (event.modifierFlags & WebEventFlagMaskShift)
61         modifiers.add(PlatformEvent::Modifier::ShiftKey);
62     if (event.modifierFlags & WebEventFlagMaskControl)
63         modifiers.add(PlatformEvent::Modifier::CtrlKey);
64     if (event.modifierFlags & WebEventFlagMaskAlternate)
65         modifiers.add(PlatformEvent::Modifier::AltKey);
66     if (event.modifierFlags & WebEventFlagMaskCommand)
67         modifiers.add(PlatformEvent::Modifier::MetaKey);
68     if (event.modifierFlags & WebEventFlagMaskAlphaShift)
69         modifiers.add(PlatformEvent::Modifier::CapsLockKey);
70
71     return modifiers;
72 }
73
74 static inline IntPoint pointForEvent(WebEvent *event)
75 {
76     return IntPoint(event.locationInWindow);
77 }
78
79 static inline IntPoint globalPointForEvent(WebEvent *event)
80 {
81     // iOS WebKit works as if it is full screen. Therefore Web coords are Global coords.
82     return pointForEvent(event);
83 }
84
85 static PlatformEvent::Type mouseEventType(WebEvent *event)
86 {
87     switch (event.type) {
88     case WebEventMouseDown:
89         return PlatformEvent::MousePressed;
90     case WebEventMouseUp:
91         return PlatformEvent::MouseReleased;
92     case WebEventMouseMoved:
93         return PlatformEvent::MouseMoved;
94     default:
95         ASSERT_NOT_REACHED();
96         return PlatformEvent::MousePressed;
97     }
98 }
99
100 static std::optional<int> convertSpecialKeyToCharCode(NSString *keyString)
101 {
102     if ([keyString isEqualToString:UIKeyInputUpArrow])
103         return NSUpArrowFunctionKey;
104     if ([keyString isEqualToString:UIKeyInputDownArrow])
105         return NSDownArrowFunctionKey;
106     if ([keyString isEqualToString:UIKeyInputLeftArrow])
107         return NSLeftArrowFunctionKey;
108     if ([keyString isEqualToString:UIKeyInputRightArrow])
109         return NSRightArrowFunctionKey;
110     return std::nullopt;
111 }
112
113 int keyCodeForEvent(WebEvent *event)
114 {
115     if ([event.characters isEqualToString:UIKeyInputUpArrow])
116         return VK_UP;
117     if ([event.characters isEqualToString:UIKeyInputDownArrow])
118         return VK_DOWN;
119     if ([event.characters isEqualToString:UIKeyInputLeftArrow])
120         return VK_LEFT;
121     if ([event.characters isEqualToString:UIKeyInputRightArrow])
122         return VK_RIGHT;
123     return event.keyCode;
124 }
125
126 class PlatformMouseEventBuilder : public PlatformMouseEvent {
127 public:
128     PlatformMouseEventBuilder(WebEvent *event)
129     {
130         m_type = mouseEventType(event);
131         m_timestamp = WallTime::now();
132
133         m_position = pointForEvent(event);
134         m_globalPosition = globalPointForEvent(event);
135         m_button = LeftButton; // This has always been the LeftButton on iOS.
136         m_clickCount = 1; // This has always been 1 on iOS.
137     }
138 };
139
140 PlatformMouseEvent PlatformEventFactory::createPlatformMouseEvent(WebEvent *event)
141 {
142     return PlatformMouseEventBuilder(event);
143 }
144
145 class PlatformWheelEventBuilder : public PlatformWheelEvent {
146 public:
147     PlatformWheelEventBuilder(WebEvent *event)
148     {
149         ASSERT(event.type == WebEventScrollWheel);
150
151         m_type = PlatformEvent::Wheel;
152         m_timestamp = WallTime::now();
153
154         m_position = pointForEvent(event);
155         m_globalPosition = globalPointForEvent(event);
156         m_deltaX = event.deltaX;
157         m_deltaY = event.deltaY;
158         m_granularity = ScrollByPixelWheelEvent; // iOS only supports continuous (pixel-mode) scrolling.
159     }
160 };
161
162 PlatformWheelEvent PlatformEventFactory::createPlatformWheelEvent(WebEvent *event)
163 {
164     return PlatformWheelEventBuilder(event);
165 }
166
167 String keyIdentifierForKeyEvent(WebEvent *event)
168 {
169     NSString *characters = event.charactersIgnoringModifiers;
170     if (auto specialKeyCharCode = convertSpecialKeyToCharCode(characters))
171         return keyIdentifierForCharCode(*specialKeyCharCode);
172     if ([characters length] != 1) {
173         LOG(Events, "received an unexpected number of characters in key event: %u", [characters length]);
174         return "Unidentified"_s;
175     }
176     return keyIdentifierForCharCode([characters characterAtIndex:0]);
177 }
178
179 String keyForKeyEvent(WebEvent *event)
180 {
181     NSString *characters = event.characters;
182     auto length = [characters length];
183     // characters return an empty string for dead keys.
184     // https://developer.apple.com/reference/appkit/nsevent/1534183-characters
185     // "Dead" is defined here https://w3c.github.io/uievents-key/#keys-composition.
186     if (!length)
187         return "Dead"_s;
188     if (auto specialKeyCharCode = convertSpecialKeyToCharCode(characters))
189         return keyForCharCode(*specialKeyCharCode);
190     if (length > 1)
191         return characters;
192     return keyForCharCode([characters characterAtIndex:0]);
193 }
194
195 // https://w3c.github.io/uievents-code/
196 String codeForKeyEvent(WebEvent *event)
197 {
198     switch (keyCodeForEvent(event)) {
199     // Keys in the alphanumeric section.
200     case VK_OEM_3: return "Backquote"_s;
201     case VK_OEM_5: return "Backslash"_s;
202     case VK_BACK: return "Backspace"_s;
203     case VK_OEM_4: return "BracketLeft"_s;
204     case VK_OEM_6: return "BracketRight"_s;
205     case VK_OEM_COMMA: return "Comma"_s;
206     case VK_0: return "Digit0"_s;
207     case VK_1: return "Digit1"_s;
208     case VK_2: return "Digit2"_s;
209     case VK_3: return "Digit3"_s;
210     case VK_4: return "Digit4"_s;
211     case VK_5: return "Digit5"_s;
212     case VK_6: return "Digit6"_s;
213     case VK_7: return "Digit7"_s;
214     case VK_8: return "Digit8"_s;
215     case VK_9: return "Digit9"_s;
216     case VK_OEM_PLUS: return "Equal"_s;
217     case VK_OEM_102: return "IntlBackslash"_s;
218     // IntlRo.
219     // IntlYen.
220     case VK_A: return "KeyA"_s;
221     case VK_B: return "KeyB"_s;
222     case VK_C: return "KeyC"_s;
223     case VK_D: return "KeyD"_s;
224     case VK_E: return "KeyE"_s;
225     case VK_F: return "KeyF"_s;
226     case VK_G: return "KeyG"_s;
227     case VK_H: return "KeyH"_s;
228     case VK_I: return "KeyI"_s;
229     case VK_J: return "KeyJ"_s;
230     case VK_K: return "KeyK"_s;
231     case VK_L: return "KeyL"_s;
232     case VK_M: return "KeyM"_s;
233     case VK_N: return "KeyN"_s;
234     case VK_O: return "KeyO"_s;
235     case VK_P: return "KeyP"_s;
236     case VK_Q: return "KeyQ"_s;
237     case VK_R: return "KeyR"_s;
238     case VK_S: return "KeyS"_s;
239     case VK_T: return "KeyT"_s;
240     case VK_U: return "KeyU"_s;
241     case VK_V: return "KeyV"_s;
242     case VK_W: return "KeyW"_s;
243     case VK_X: return "KeyX"_s;
244     case VK_Y: return "KeyY"_s;
245     case VK_Z: return "KeyZ"_s;
246     case VK_OEM_MINUS: return "Minus"_s;
247     case VK_OEM_PERIOD: return "Period"_s;
248     case VK_OEM_7: return "Quote"_s;
249     case VK_OEM_1: return "Semicolon"_s;
250     case VK_OEM_2: return "Slash"_s;
251
252     // Functional keys in alphanumeric section.
253     case VK_MENU: return "AltLeft"_s;
254     // AltRight.
255     case VK_CAPITAL: return "CapsLock"_s;
256     // ContextMenu.
257     case VK_LCONTROL: return "ControlLeft"_s;
258     case VK_RCONTROL: return "ControlRight"_s;
259     case VK_RETURN: return "Enter"_s; //  Labeled Return on Apple keyboards.
260     case VK_LWIN: return "MetaLeft"_s;
261     case VK_RWIN: return "MetaRight"_s;
262     case VK_LSHIFT: return "ShiftLeft"_s;
263     case VK_RSHIFT: return "ShiftRight"_s;
264     case VK_SPACE: return "Space"_s;
265     case VK_TAB: return "Tab"_s;
266
267     // Functional keys found on Japanese and Korean keyboards.
268     // Convert.
269     case VK_KANA: return "KanaMode"_s;
270     // Lang1.
271     // Lang2.
272     // Lang3.
273     // Lang4.
274     // Lang5.
275     // NonConvert.
276
277     // Keys in the ControlPad section.
278     // Delete
279     case VK_END: return "End"_s;
280     case VK_HELP: return "Help"_s;
281     case VK_HOME: return "Home"_s;
282     // Insert: Not present on Apple keyboards.
283     case VK_NEXT: return "PageDown"_s;
284     case VK_PRIOR: return "PageUp"_s;
285
286     // Keys in the ArrowPad section.
287     case VK_DOWN: return "ArrowDown"_s;
288     case VK_LEFT: return "ArrowLeft"_s;
289     case VK_RIGHT: return "ArrowRight"_s;
290     case VK_UP: return "ArrowUp"_s;
291
292     // Keys in the Numpad section.
293     case VK_NUMLOCK: return "NumLock"_s;
294     case VK_NUMPAD0: return "Numpad0"_s;
295     case VK_NUMPAD1: return "Numpad1"_s;
296     case VK_NUMPAD2: return "Numpad2"_s;
297     case VK_NUMPAD3: return "Numpad3"_s;
298     case VK_NUMPAD4: return "Numpad4"_s;
299     case VK_NUMPAD5: return "Numpad5"_s;
300     case VK_NUMPAD6: return "Numpad6"_s;
301     case VK_NUMPAD7: return "Numpad7"_s;
302     case VK_NUMPAD8: return "Numpad8"_s;
303     case VK_NUMPAD9: return "Numpad9"_s;
304     case VK_ADD: return "NumpadAdd"_s;
305     // NumpadBackspace.
306     // NumpadClear.
307     // NumpadClearEntry.
308     case VK_SEPARATOR: return "NumpadComma"_s;
309     case VK_DECIMAL: return "NumpadDecimal"_s;
310     case VK_DIVIDE: return "NumpadDivide"_s;
311     // NumpadEnter.
312     case VK_CLEAR: return "NumpadEqual"_s;
313     // NumpadHash.
314     // NumpadMemoryAdd.
315     // NumpadMemoryClear.
316     // NumpadMemoryRecall.
317     // NumpadMemoryStore.
318     // NumpadMemorySubtract.
319     case VK_MULTIPLY: return "NumpadMultiply"_s;
320     // NumpadParenLeft.
321     // NumpadParenRight.
322     // NumpadStar: The specification says to use "NumpadMultiply" for the * key on numeric keypads.
323     case VK_SUBTRACT: return "NumpadSubtract"_s;
324
325     // Keys in the Function section.
326     case VK_ESCAPE: return "Escape"_s;
327     case VK_F1: return "F1"_s;
328     case VK_F2: return "F2"_s;
329     case VK_F3: return "F3"_s;
330     case VK_F4: return "F4"_s;
331     case VK_F5: return "F5"_s;
332     case VK_F6: return "F6"_s;
333     case VK_F7: return "F7"_s;
334     case VK_F8: return "F8"_s;
335     case VK_F9: return "F9"_s;
336     case VK_F10: return "F10"_s;
337     case VK_F11: return "F11"_s;
338     case VK_F12: return "F12"_s;
339     case VK_F13: return "F13"_s;
340     case VK_F14: return "F14"_s;
341     case VK_F15: return "F15"_s;
342     case VK_F16: return "F16"_s;
343     case VK_F17: return "F17"_s;
344     case VK_F18: return "F18"_s;
345     case VK_F19: return "F19"_s;
346     case VK_F20: return "F20"_s;
347     // Fn: This is typically a hardware key that does not generate a separate code.
348     // FnLock.
349     // PrintScreen.
350     // ScrollLock.
351     // Pause.
352
353     // Media keys.
354     // BrowserBack.
355     // BrowserFavorites.
356     // BrowserForward.
357     // BrowserHome.
358     // BrowserRefresh.
359     // BrowserSearch.
360     // BrowserStop.
361     // Eject.
362     // LaunchApp1.
363     // LaunchApp2.
364     // LaunchMail.
365     // MediaPlayPause.
366     // MediaSelect.
367     // MediaStop.
368     // MediaTrackNext.
369     // MediaTrackPrevious.
370     // Power.
371     // Sleep.
372     case VK_VOLUME_DOWN: return "AudioVolumeDown"_s;
373     case VK_VOLUME_MUTE: return "AudioVolumeMute"_s;
374     case VK_VOLUME_UP: return "AudioVolumeUp"_s;
375     // WakeUp.
376
377     // Legacy modifier keys.
378     // Hyper.
379     // Super.
380     // Turbo.
381
382     // Legacy process control keys.
383     // Abort.
384     // Resume.
385     // Suspend.
386
387     // Legacy editing keys.
388     // Again.
389     // Copy.
390     // Cut.
391     // Find.
392     // Open.
393     // Paste.
394     // Props.
395     // Select.
396     // Undo.
397
398     // Keys found on international keyboards.
399     // Hiragana.
400     // Katakana.
401
402     default:
403         return "Unidentified"_s;
404     }
405 }
406
407 class PlatformKeyboardEventBuilder : public PlatformKeyboardEvent {
408 public:
409     PlatformKeyboardEventBuilder(WebEvent *event)
410     {
411         ASSERT(event.type == WebEventKeyDown || event.type == WebEventKeyUp);
412
413         m_type = (event.type == WebEventKeyUp ? PlatformEvent::KeyUp : PlatformEvent::KeyDown);
414         m_modifiers = modifiersForEvent(event);
415         m_timestamp = WallTime::now();
416
417         m_text = event.characters;
418         m_unmodifiedText = event.charactersIgnoringModifiers;
419         m_key = keyForKeyEvent(event);
420         m_code = codeForKeyEvent(event);
421         m_keyIdentifier = keyIdentifierForKeyEvent(event);
422         m_windowsVirtualKeyCode = keyCodeForEvent(event);
423         m_autoRepeat = event.isKeyRepeating;
424         m_isKeypad = false; // iOS does not distinguish the numpad. See <rdar://problem/7190835>.
425         m_isSystemKey = false;
426         m_Event = event;
427
428         // Always use 13 for Enter/Return -- we don't want to use AppKit's different character for Enter.
429         if (m_windowsVirtualKeyCode == '\r') {
430             m_text = "\r";
431             m_unmodifiedText = "\r";
432         }
433
434         // The adjustments below are only needed in backward compatibility mode, but we cannot tell what mode we are in from here.
435
436         // Turn 0x7F into 8, because backspace needs to always be 8.
437         if (m_text == "\x7F")
438             m_text = "\x8";
439         if (m_unmodifiedText == "\x7F")
440             m_unmodifiedText = "\x8";
441         // Always use 9 for tab -- we don't want to use AppKit's different character for shift-tab.
442         if (m_windowsVirtualKeyCode == 9) {
443             m_text = "\x9";
444             m_unmodifiedText = "\x9";
445         }
446     }
447 };
448
449 PlatformKeyboardEvent PlatformEventFactory::createPlatformKeyboardEvent(WebEvent *event)
450 {
451     return PlatformKeyboardEventBuilder(event);
452 }
453
454 #if ENABLE(TOUCH_EVENTS)
455 static PlatformTouchPoint::TouchPhaseType convertTouchPhase(NSNumber *touchPhaseNumber)
456 {
457     WebEventTouchPhaseType touchPhase = static_cast<WebEventTouchPhaseType>([touchPhaseNumber unsignedIntValue]);
458     switch (touchPhase) {
459     case WebEventTouchPhaseBegan:
460         return PlatformTouchPoint::TouchPhaseBegan;
461     case WebEventTouchPhaseMoved:
462         return PlatformTouchPoint::TouchPhaseMoved;
463     case WebEventTouchPhaseStationary:
464         return PlatformTouchPoint::TouchPhaseStationary;
465     case WebEventTouchPhaseEnded:
466         return PlatformTouchPoint::TouchPhaseEnded;
467     case WebEventTouchPhaseCancelled:
468         return PlatformTouchPoint::TouchPhaseCancelled;
469     default:
470         ASSERT_NOT_REACHED();
471     }
472     return PlatformTouchPoint::TouchPhaseBegan;
473 }
474
475 static PlatformEvent::Type touchEventType(WebEvent *event)
476 {
477     switch (event.type) {
478     case WebEventTouchBegin:
479         return PlatformEvent::TouchStart;
480     case WebEventTouchEnd:
481         return PlatformEvent::TouchEnd;
482     case WebEventTouchCancel:
483         return PlatformEvent::TouchCancel;
484     case WebEventTouchChange:
485         return PlatformEvent::TouchMove;
486     default:
487         ASSERT_NOT_REACHED();
488         return PlatformEvent::TouchCancel;
489     }
490 }
491     
492 static PlatformTouchPoint::TouchPhaseType touchPhaseFromPlatformEventType(PlatformEvent::Type type)
493 {
494     switch (type) {
495     case PlatformEvent::TouchStart:
496         return PlatformTouchPoint::TouchPhaseBegan;
497     case PlatformEvent::TouchMove:
498         return PlatformTouchPoint::TouchPhaseMoved;
499     case PlatformEvent::TouchEnd:
500         return PlatformTouchPoint::TouchPhaseEnded;
501     default:
502         ASSERT_NOT_REACHED();
503         return PlatformTouchPoint::TouchPhaseCancelled;
504     }
505 }
506
507 class PlatformTouchPointBuilder : public PlatformTouchPoint {
508 public:
509     PlatformTouchPointBuilder(unsigned identifier, const IntPoint& location, TouchPhaseType phase)
510         : PlatformTouchPoint(identifier, location, phase)
511     {
512     }
513 };
514
515 class PlatformTouchEventBuilder : public PlatformTouchEvent {
516 public:
517     PlatformTouchEventBuilder(WebEvent *event)
518     {
519         m_type = touchEventType(event);
520         m_modifiers = modifiersForEvent(event);
521         m_timestamp = WallTime::fromRawSeconds(event.timestamp);
522
523         m_gestureScale = event.gestureScale;
524         m_gestureRotation = event.gestureRotation;
525         m_isGesture = event.isGesture;
526         m_position = pointForEvent(event);
527         m_globalPosition = globalPointForEvent(event);
528
529         unsigned touchCount = event.touchCount;
530         m_touchPoints.reserveInitialCapacity(touchCount);
531         for (unsigned i = 0; i < touchCount; ++i) {
532             unsigned identifier = [(NSNumber *)[event.touchIdentifiers objectAtIndex:i] unsignedIntValue];
533             IntPoint location = IntPoint([(NSValue *)[event.touchLocations objectAtIndex:i] pointValue]);
534             PlatformTouchPoint::TouchPhaseType touchPhase = convertTouchPhase([event.touchPhases objectAtIndex:i]);
535             m_touchPoints.uncheckedAppend(PlatformTouchPointBuilder(identifier, location, touchPhase));
536         }
537     }
538     
539     PlatformTouchEventBuilder(PlatformEvent::Type type, IntPoint location)
540     {
541         m_type = type;
542         m_timestamp = WallTime::now();
543         
544         m_gestureScale = 1;
545         m_gestureRotation = 0;
546         m_isGesture = 0;
547         m_position = location;
548         m_globalPosition = location;
549         m_isPotentialTap = true;
550         
551         unsigned touchCount = 1;
552         m_touchPoints.reserveInitialCapacity(touchCount);
553         for (unsigned i = 0; i < touchCount; ++i)
554             m_touchPoints.uncheckedAppend(PlatformTouchPointBuilder(1, location, touchPhaseFromPlatformEventType(type)));
555     }
556 };
557
558 PlatformTouchEvent PlatformEventFactory::createPlatformTouchEvent(WebEvent *event)
559 {
560     return PlatformTouchEventBuilder(event);
561 }
562     
563 PlatformTouchEvent PlatformEventFactory::createPlatformSimulatedTouchEvent(PlatformEvent::Type type, IntPoint location)
564 {
565     return PlatformTouchEventBuilder(type, location);
566 }
567
568 #endif // ENABLE(TOUCH_EVENTS)
569
570 } // namespace WebCore
571
572 #endif // PLATFORM(IOS)