[iOS] Special keys are misidentified in DOM keyboard events
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 1 Oct 2018 18:35:37 +0000 (18:35 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 1 Oct 2018 18:35:37 +0000 (18:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189974

Reviewed by Wenson Hsieh.

Source/WebCore:

This patch fixes two issues:
    1. Special keyboard keys would be misidentified in dispatched DOM keyboard events.
    2. DOM keypress events may not be dispatched for some special keys.

UIKit uses special input strings to identify the Page Up, Page Down, Escape, Up Arrow, Down Arrow,
Left Arrow, and Right Arrow keys. It also uses ASCII control characters to represent some other
special keys, including Num Lock / Clear, Home, End, Forward Delete, and F1, ..., F24. We need
to explicitly handle these special keyboard keys in order to be able to identify the key that
was pressed as well as to correctly disambiguate a key down to know whether to dispatch a DOM
keypress event for the key.

Unlike UIKit, AppKit reserves Unicode Private Use Area (PUA) code points in 0xF700–0xF8FF to
represent special keyboard keys. This makes it straightforward to disambiguate such keys using
the input string of the keyboard event alone. To simplify the implementation for iOS
we normalize the input string be AppKit compatible. See the explaination for WebCore::windowsKeyCodeForCharCode()
below for more details on why this is done.

Tests: fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html
       fast/events/ios/keypress-keys-in-non-editable-element.html

* SourcesCocoa.txt:
* WebCore.xcodeproj/project.pbxproj:
Do not use unified source build strategy when building WebEvent.mm as it makes
use of SoftLinking macros that are incompatible with this strategy.

* platform/ios/KeyEventIOS.mm:
(WebCore::windowsKeyCodeForCharCode): Recognize some special AppKit special char codes.
These special char codes are generated by WebKit. WebKit uses the same special char codes
as AppKit as a convenience instead of defining our own constants for the same purpose.
Encoding the special UIKit input strings (e.g. up arrow) as distinct char codes allows us
to use integer arithmetic and switch blocks to map characters to Windows virtual key
codes as opposed to special cased branches to perform pointer or string comparisions.
The latter would be necessary in Modern WebKit in order for key down events to be properly
disambiguated to dispatch a DOM keypress event because pointers are not perserved, though
what they point to is, when sending the WebEvent from UIProcess to the WebProcess and
vice versa.
(WebCore::isFunctionKey): Convenience function that determines whether the specified char
code corresponds to a function key on the keyboard. The term "function key" is taken from
AppKit parlance to describe a special keyboard key. These keys include F1, F2, ..., F24,
and cursor keys among other special keyboard keys.
(WebCore::PlatformKeyboardEvent::disambiguateKeyDownEvent): Write in terms of isFunctionKey().
* platform/ios/PlatformEventFactoryIOS.h:
* platform/ios/PlatformEventFactoryIOS.mm:
(WebCore::keyIdentifierForKeyEvent): Remove code to handle UIKit special input strings as
we now map such special input strings to char codes and hence can use the default code path.
(WebCore::keyForKeyEvent): Ditto.
(WebCore::codeForKeyEvent): Remove code to compute the Window virtual key code corresponding
to a UIKit special key command now that we map such special input strings to char codes and
subsequently map the char codes to the Windows virtual key code (see -[WebEvent initWithKeyEventType:...]
constructors). So, we can now use WebEvent.keyCode directly to compute the DOM UIEvents code
for the event.
(WebCore::PlatformKeyboardEventBuilder::PlatformKeyboardEventBuilder): Remove code to fix up
WebEvent.keyCode to account for UIKit special input strings now that we map such special key
commands to char codes and subsequently map the char codes to the Windows virtual key code (see -[WebEvent initWithKeyEventType:...]
constructors). So, we can now take WebEvent.keyCode verbatim to be the Window virtual key code.
(WebCore::convertSpecialKeyToCharCode): Deleted.
(WebCore::keyCodeForEvent): Deleted.
* platform/ios/WebEvent.mm:
(normalizedStringWithAppKitCompatibilityMapping): Added; converts a UIKit character string
to the corresponding AppKit-compatible one (if not already compatible). See the explaination
for WebCore::windowsKeyCodeForCharCode() above for more details on why this is done.

(-[WebEvent initWithKeyEventType:timeStamp:characters:charactersIgnoringModifiers:modifiers:isRepeating:withFlags:keyCode:isTabKey:characterSet:]):
(-[WebEvent initWithKeyEventType:timeStamp:characters:charactersIgnoringModifiers:modifiers:isRepeating:withFlags:withInputManagerHint:keyCode:isTabKey:]):
Normalize the character strings to be AppKit compatible.

Source/WebCore/PAL:

Forward declare or define more SPI.

* pal/spi/cocoa/IOKitSPI.h:
* pal/spi/ios/UIKitSPI.h:

Source/WebKit:

Take the key code of WebEvent to be the key code for the new WebKeyboardEvent verbatim
now that we normalize the character strings of the WebEvent to account for the special
UIKit input strings.

* Shared/ios/WebIOSEventFactory.mm:
(WebIOSEventFactory::createWebKeyboardEvent):

Tools:

Add support for testing keys Forward Delete and Num Lock / Clear.

* WebKitTestRunner/ios/HIDEventGenerator.mm:
(hidUsageCodeForCharacter):

LayoutTests:

Add tests to ensure that we do not regress key identification for special keys.

Update the expected results for test fast/events/ios/keydown-keyup-special-keys-in-non-editable-element.html
now that we correctly identify some more keys.

* fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element-expected.txt: Added.
* fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html: Added.
* fast/events/ios/keydown-keyup-special-keys-in-non-editable-element-expected.txt:
* fast/events/ios/keypress-keys-in-non-editable-element-expected.txt: Added.
* fast/events/ios/keypress-keys-in-non-editable-element.html: Added.
* resources/ui-helper.js:
(window.UIHelper.typeCharacter): Actually type the specified character in DumpRenderTree.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@236678 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html [new file with mode: 0644]
LayoutTests/fast/events/ios/keydown-keyup-special-keys-in-non-editable-element-expected.txt
LayoutTests/fast/events/ios/keypress-keys-in-non-editable-element-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/ios/keypress-keys-in-non-editable-element.html [new file with mode: 0644]
LayoutTests/resources/ui-helper.js
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/spi/cocoa/IOKitSPI.h
Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h
Source/WebCore/SourcesCocoa.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/ios/KeyEventIOS.mm
Source/WebCore/platform/ios/PlatformEventFactoryIOS.h
Source/WebCore/platform/ios/PlatformEventFactoryIOS.mm
Source/WebCore/platform/ios/WebEvent.mm
Source/WebKit/ChangeLog
Source/WebKit/Shared/ios/WebIOSEventFactory.mm
Tools/ChangeLog
Tools/WebKitTestRunner/ios/HIDEventGenerator.mm

index 40ad1cc..f988257 100644 (file)
@@ -1,3 +1,23 @@
+2018-10-01  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Special keys are misidentified in DOM keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=189974
+
+        Reviewed by Wenson Hsieh.
+
+        Add tests to ensure that we do not regress key identification for special keys.
+
+        Update the expected results for test fast/events/ios/keydown-keyup-special-keys-in-non-editable-element.html
+        now that we correctly identify some more keys.
+
+        * fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element-expected.txt: Added.
+        * fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html: Added.
+        * fast/events/ios/keydown-keyup-special-keys-in-non-editable-element-expected.txt:
+        * fast/events/ios/keypress-keys-in-non-editable-element-expected.txt: Added.
+        * fast/events/ios/keypress-keys-in-non-editable-element.html: Added.
+        * resources/ui-helper.js:
+        (window.UIHelper.typeCharacter): Actually type the specified character in DumpRenderTree.
+
 2018-10-01  Ryan Haddad  <ryanhaddad@apple.com>
 
         REGRESSION (r235948?): Layout Test media/media-controls-accessibility.html is a flaky failure
diff --git a/LayoutTests/fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element-expected.txt b/LayoutTests/fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element-expected.txt
new file mode 100644 (file)
index 0000000..78a68e9
--- /dev/null
@@ -0,0 +1,11 @@
+This tests that DOM keydown and keyup events are dispatched to a non-editable <body> on iOS when pressing the arrow keys on a hardware keyboard. To run this test manually, verify that two messages are emitted when you press the following keys: ↑, ↓, ←, →.
+
+type: keydown, key: ArrowUp, code: ArrowUp, keyIdentifier: Up, keyCode: 38, charCode: 0, keyCode: 38, which: 38
+type: keyup, key: ArrowUp, code: ArrowUp, keyIdentifier: Up, keyCode: 38, charCode: 0, keyCode: 38, which: 38
+type: keydown, key: ArrowDown, code: ArrowDown, keyIdentifier: Down, keyCode: 40, charCode: 0, keyCode: 40, which: 40
+type: keyup, key: ArrowDown, code: ArrowDown, keyIdentifier: Down, keyCode: 40, charCode: 0, keyCode: 40, which: 40
+type: keydown, key: ArrowLeft, code: ArrowLeft, keyIdentifier: Left, keyCode: 37, charCode: 0, keyCode: 37, which: 37
+type: keyup, key: ArrowLeft, code: ArrowLeft, keyIdentifier: Left, keyCode: 37, charCode: 0, keyCode: 37, which: 37
+type: keydown, key: ArrowRight, code: ArrowRight, keyIdentifier: Right, keyCode: 39, charCode: 0, keyCode: 39, which: 39
+type: keyup, key: ArrowRight, code: ArrowRight, keyIdentifier: Right, keyCode: 39, charCode: 0, keyCode: 39, which: 39
+
diff --git a/LayoutTests/fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html b/LayoutTests/fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html
new file mode 100644 (file)
index 0000000..cf92506
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width">
+<script src="../../../resources/ui-helper.js"></script>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body onkeydown="handleKeyDown(event)" onkeyup="handleKeyUp(event)">
+<p>This tests that DOM keydown and keyup events are dispatched to a non-editable &lt;body&gt; on iOS when pressing the arrow keys on a hardware keyboard. To run this test manually, verify that two messages are emitted when you press the following keys: &#x2191;, &#x2193;, &#x2190;, &#x2192;.</p>
+<pre id="console"></pre>
+<script>
+var remainingKeysToPress = ["upArrow", "downArrow", "leftArrow", "rightArrow"]
+
+async function nextKeyPress()
+{
+    if (!remainingKeysToPress.length) {
+        if (window.testRunner)
+            testRunner.notifyDone();
+        return;
+    }
+    let nextKey = remainingKeysToPress.shift();
+    UIHelper.typeCharacter(nextKey);
+}
+
+function handleKeyDown(event)
+{
+    logKeyEvent(event);
+}
+
+function handleKeyUp(event)
+{
+    logKeyEvent(event);
+    nextKeyPress();
+}
+
+function log(message)
+{
+    document.getElementById("console").appendChild(document.createTextNode(message + "\n"));
+}
+
+function logKeyEvent(event)
+{
+    let pieces = [];
+    for (let propertyName of ["type", "key", "code", "keyIdentifier", "keyCode", "charCode", "keyCode", "which"])
+        pieces.push(`${propertyName}: ${event[propertyName]}`);
+    log(pieces.join(", "));
+}
+
+function runTest()
+{
+    if (!window.testRunner)
+        return;
+    nextKeyPress();
+}
+
+runTest();
+</script>
+</body>
+</html>
index 333f055..43a27ab 100644 (file)
@@ -2,9 +2,11 @@ This tests that DOM keydown and keyup events are dispatched to a non-editable <b
 
 type: keydown, key: ArrowUp, code: ArrowUp, keyIdentifier: Up, keyCode: 38, charCode: 0, keyCode: 38, which: 38
 type: keyup, key: ArrowUp, code: ArrowUp, keyIdentifier: Up, keyCode: 38, charCode: 0, keyCode: 38, which: 38
+type: keydown, key: ArrowDown, code: ArrowDown, keyIdentifier: Down, keyCode: 40, charCode: 0, keyCode: 40, which: 40
 type: keyup, key: ArrowDown, code: ArrowDown, keyIdentifier: Down, keyCode: 40, charCode: 0, keyCode: 40, which: 40
 type: keydown, key: ArrowLeft, code: ArrowLeft, keyIdentifier: Left, keyCode: 37, charCode: 0, keyCode: 37, which: 37
 type: keyup, key: ArrowLeft, code: ArrowLeft, keyIdentifier: Left, keyCode: 37, charCode: 0, keyCode: 37, which: 37
+type: keydown, key: ArrowRight, code: ArrowRight, keyIdentifier: Right, keyCode: 39, charCode: 0, keyCode: 39, which: 39
 type: keyup, key: ArrowRight, code: ArrowRight, keyIdentifier: Right, keyCode: 39, charCode: 0, keyCode: 39, which: 39
 type: keydown, key: Backspace, code: Backspace, keyIdentifier: U+0008, keyCode: 8, charCode: 0, keyCode: 8, which: 8
 type: keyup, key: Dead, code: Backspace, keyIdentifier: Unidentified, keyCode: 8, charCode: 0, keyCode: 8, which: 8
@@ -12,8 +14,8 @@ type: keydown, key: \ 4, code: End, keyIdentifier: U+0004, keyCode: 35, charCode:
 type: keyup, key: Dead, code: End, keyIdentifier: Unidentified, keyCode: 35, charCode: 0, keyCode: 35, which: 35
 type: keydown, key: Enter, code: Enter, keyIdentifier: Enter, keyCode: 13, charCode: 0, keyCode: 13, which: 13
 type: keyup, key: Dead, code: Enter, keyIdentifier: Unidentified, keyCode: 13, charCode: 0, keyCode: 13, which: 13
-type: keydown, key: UIKeyInputEscape, code: Escape, keyIdentifier: Unidentified, keyCode: 27, charCode: 0, keyCode: 27, which: 27
-type: keyup, key: UIKeyInputEscape, code: Escape, keyIdentifier: Unidentified, keyCode: 27, charCode: 0, keyCode: 27, which: 27
+type: keydown, key: Escape, code: Escape, keyIdentifier: U+001B, keyCode: 27, charCode: 0, keyCode: 27, which: 27
+type: keyup, key: Escape, code: Escape, keyIdentifier: U+001B, keyCode: 27, charCode: 0, keyCode: 27, which: 27
 type: keydown, key: \ 1, code: Home, keyIdentifier: U+0001, keyCode: 36, charCode: 0, keyCode: 36, which: 36
 type: keyup, key: Dead, code: Home, keyIdentifier: Unidentified, keyCode: 36, charCode: 0, keyCode: 36, which: 36
 type: keydown, key: Dead, code: Unidentified, keyIdentifier: Unidentified, keyCode: 45, charCode: 0, keyCode: 45, which: 45
@@ -26,9 +28,10 @@ type: keydown, key: Dead, code: ControlLeft, keyIdentifier: Unidentified, keyCod
 type: keyup, key: Dead, code: ControlLeft, keyIdentifier: Unidentified, keyCode: 17, charCode: 0, keyCode: 17, which: 17
 type: keydown, key: Dead, code: ShiftLeft, keyIdentifier: Unidentified, keyCode: 16, charCode: 0, keyCode: 16, which: 16
 type: keyup, key: Dead, code: ShiftLeft, keyIdentifier: Unidentified, keyCode: 16, charCode: 0, keyCode: 16, which: 16
-type: keydown, key: UIKeyInputPageDown, code: PageDown, keyIdentifier: Unidentified, keyCode: 34, charCode: 0, keyCode: 34, which: 34
-type: keyup, key: UIKeyInputPageDown, code: PageDown, keyIdentifier: Unidentified, keyCode: 34, charCode: 0, keyCode: 34, which: 34
-type: keyup, key: UIKeyInputPageUp, code: PageUp, keyIdentifier: Unidentified, keyCode: 33, charCode: 0, keyCode: 33, which: 33
+type: keydown, key: PageDown, code: PageDown, keyIdentifier: PageDown, keyCode: 34, charCode: 0, keyCode: 34, which: 34
+type: keyup, key: PageDown, code: PageDown, keyIdentifier: PageDown, keyCode: 34, charCode: 0, keyCode: 34, which: 34
+type: keydown, key: PageUp, code: PageUp, keyIdentifier: PageUp, keyCode: 33, charCode: 0, keyCode: 33, which: 33
+type: keyup, key: PageUp, code: PageUp, keyIdentifier: PageUp, keyCode: 33, charCode: 0, keyCode: 33, which: 33
 type: keydown, key: Enter, code: Enter, keyIdentifier: Enter, keyCode: 13, charCode: 0, keyCode: 13, which: 13
 type: keyup, key: Dead, code: Enter, keyIdentifier: Unidentified, keyCode: 13, charCode: 0, keyCode: 13, which: 13
 type: keydown, key: Dead, code: Unidentified, keyIdentifier: Unidentified, keyCode: 18, charCode: 0, keyCode: 18, which: 18
diff --git a/LayoutTests/fast/events/ios/keypress-keys-in-non-editable-element-expected.txt b/LayoutTests/fast/events/ios/keypress-keys-in-non-editable-element-expected.txt
new file mode 100644 (file)
index 0000000..d064128
--- /dev/null
@@ -0,0 +1,103 @@
+This tests that DOM keypress events are dispatched to a non-editable <body> on iOS when pressing special keys on a hardware keyboard. This test can only be run in DumpRenderTree/WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS a did dispatch a keypress event.
+PASS b did dispatch a keypress event.
+PASS c did dispatch a keypress event.
+PASS d did dispatch a keypress event.
+PASS e did dispatch a keypress event.
+PASS f did dispatch a keypress event.
+PASS g did dispatch a keypress event.
+PASS h did dispatch a keypress event.
+PASS i did dispatch a keypress event.
+PASS j did dispatch a keypress event.
+PASS k did dispatch a keypress event.
+PASS l did dispatch a keypress event.
+PASS m did dispatch a keypress event.
+PASS n did dispatch a keypress event.
+PASS o did dispatch a keypress event.
+PASS p did dispatch a keypress event.
+PASS q did dispatch a keypress event.
+PASS r did dispatch a keypress event.
+PASS s did dispatch a keypress event.
+PASS t did dispatch a keypress event.
+PASS u did dispatch a keypress event.
+PASS v did dispatch a keypress event.
+PASS w did dispatch a keypress event.
+PASS x did dispatch a keypress event.
+PASS y did dispatch a keypress event.
+PASS z did dispatch a keypress event.
+PASS 0 did dispatch a keypress event.
+PASS 1 did dispatch a keypress event.
+PASS 2 did dispatch a keypress event.
+PASS 3 did dispatch a keypress event.
+PASS 4 did dispatch a keypress event.
+PASS 5 did dispatch a keypress event.
+PASS 6 did dispatch a keypress event.
+PASS 7 did dispatch a keypress event.
+PASS 8 did dispatch a keypress event.
+PASS 9 did dispatch a keypress event.
+PASS - did dispatch a keypress event.
+PASS = did dispatch a keypress event.
+PASS [ did dispatch a keypress event.
+PASS ] did dispatch a keypress event.
+PASS \ did dispatch a keypress event.
+PASS ; did dispatch a keypress event.
+PASS ' did dispatch a keypress event.
+PASS , did dispatch a keypress event.
+PASS . did dispatch a keypress event.
+PASS / did dispatch a keypress event.
+PASS   did not dispatch a keypress event.
+PASS delete did dispatch a keypress event.
+PASS enter did dispatch a keypress event.
+PASS escape did dispatch a keypress event.
+PASS return did dispatch a keypress event.
+PASS forwardDelete did dispatch a keypress event.
+PASS upArrow did not dispatch a keypress event.
+PASS downArrow did not dispatch a keypress event.
+PASS leftArrow did not dispatch a keypress event.
+PASS rightArrow did not dispatch a keypress event.
+PASS clear did not dispatch a keypress event.
+PASS end did not dispatch a keypress event.
+PASS home did not dispatch a keypress event.
+PASS insert did not dispatch a keypress event.
+PASS leftAlt did not dispatch a keypress event.
+PASS leftCommand did not dispatch a keypress event.
+PASS leftControl did not dispatch a keypress event.
+PASS leftShift did not dispatch a keypress event.
+PASS pageDown did not dispatch a keypress event.
+PASS pageUp did not dispatch a keypress event.
+PASS rightAlt did not dispatch a keypress event.
+PASS rightCommand did not dispatch a keypress event.
+PASS rightControl did not dispatch a keypress event.
+PASS rightShift did not dispatch a keypress event.
+PASS F1 did not dispatch a keypress event.
+PASS F2 did not dispatch a keypress event.
+PASS F3 did not dispatch a keypress event.
+PASS F4 did not dispatch a keypress event.
+PASS F5 did not dispatch a keypress event.
+PASS F6 did not dispatch a keypress event.
+PASS F7 did not dispatch a keypress event.
+PASS F8 did not dispatch a keypress event.
+PASS F9 did not dispatch a keypress event.
+PASS F10 did not dispatch a keypress event.
+PASS F11 did not dispatch a keypress event.
+PASS F12 did not dispatch a keypress event.
+PASS F13 did not dispatch a keypress event.
+PASS F14 did not dispatch a keypress event.
+PASS F15 did not dispatch a keypress event.
+PASS F16 did not dispatch a keypress event.
+PASS F17 did not dispatch a keypress event.
+PASS F18 did not dispatch a keypress event.
+PASS F19 did not dispatch a keypress event.
+PASS F20 did not dispatch a keypress event.
+PASS F21 did not dispatch a keypress event.
+PASS F22 did not dispatch a keypress event.
+PASS F23 did not dispatch a keypress event.
+PASS F24 did not dispatch a keypress event.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/events/ios/keypress-keys-in-non-editable-element.html b/LayoutTests/fast/events/ios/keypress-keys-in-non-editable-element.html
new file mode 100644 (file)
index 0000000..5fd14dd
--- /dev/null
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf8">
+<meta name="viewport" content="width=device-width">
+<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/ui-helper.js"></script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("This tests that DOM keypress events are dispatched to a non-editable &lt;body&gt; on iOS when pressing special keys on a hardware keyboard. This test can only be run in DumpRenderTree/WebKitTestRunner.");
+
+class TestKeyPressDispatchedFor {
+    constructor(key) {
+        this.key = key;
+        this.shouldDispatchKeyPress = true;
+    }
+}
+
+class TestKeyPressNotDispatchedFor {
+    constructor(key) {
+        this.key = key;
+        this.shouldDispatchKeyPress = false;
+    }
+}
+
+var lastKeyPressEvent = null;
+var currentTestIndex = -1;
+
+// Non-special keys: KeyPress should be dispatched.
+var tests = "abcdefghijklmnopqrstuvwxyz0123456789-=[]\\;',./".split("").map((key) => new TestKeyPressDispatchedFor(key));
+if (UIHelper.isWebKit2())
+    tests.push(new TestKeyPressNotDispatchedFor(" ")); // Spacebar scrolls the page.
+else
+    tests.push(new TestKeyPressDispatchedFor(" "));
+// Special keys: KeyPress should be dispatched.
+tests.push(new TestKeyPressDispatchedFor("delete"));
+tests.push(new TestKeyPressDispatchedFor("enter"));
+tests.push(new TestKeyPressDispatchedFor("escape"));
+tests.push(new TestKeyPressDispatchedFor("return"));
+
+// The forward delete key is mapped to the delete key on iOS and dispatches a keypress event.
+// On Mac it is treated as its own key and does not dispatch a keypress event.
+tests.push(new TestKeyPressDispatchedFor("forwardDelete"));
+
+// Special keys: KeyPress should not be dispatched.
+tests.push(new TestKeyPressNotDispatchedFor("upArrow"));
+tests.push(new TestKeyPressNotDispatchedFor("downArrow"));
+tests.push(new TestKeyPressNotDispatchedFor("leftArrow"));
+tests.push(new TestKeyPressNotDispatchedFor("rightArrow"));
+tests.push(new TestKeyPressNotDispatchedFor("clear"));
+tests.push(new TestKeyPressNotDispatchedFor("end"));
+tests.push(new TestKeyPressNotDispatchedFor("home"));
+tests.push(new TestKeyPressNotDispatchedFor("insert"));
+tests.push(new TestKeyPressNotDispatchedFor("leftAlt"));
+tests.push(new TestKeyPressNotDispatchedFor("leftCommand"));
+tests.push(new TestKeyPressNotDispatchedFor("leftControl"));
+tests.push(new TestKeyPressNotDispatchedFor("leftShift"));
+tests.push(new TestKeyPressNotDispatchedFor("pageDown"));
+tests.push(new TestKeyPressNotDispatchedFor("pageUp"));
+tests.push(new TestKeyPressNotDispatchedFor("rightAlt"));
+tests.push(new TestKeyPressNotDispatchedFor("rightCommand"));
+tests.push(new TestKeyPressNotDispatchedFor("rightControl"));
+tests.push(new TestKeyPressNotDispatchedFor("rightShift"));
+for (let i = 1; i <= 24; ++i)
+    tests.push(new TestKeyPressNotDispatchedFor("F" + i));
+
+function nextKeyPress()
+{
+    function actualNextKeyPress() {
+        ++currentTestIndex;
+        if (currentTestIndex >= tests.length) {
+            finishJSTest();
+            return;
+        }
+        let nextTest = tests[currentTestIndex];
+        UIHelper.typeCharacter(nextTest.key);
+    }
+    // In WebKit Legacy typing a key performs a synchronous DOM dispatch. Schedule the key press
+    // on the next turn of the event loop to avoid blowing out the stack given that we call this
+    // function from an onkeyup handler.
+    window.setTimeout(actualNextKeyPress, 0);
+}
+
+function handleKeyPress(event)
+{
+    lastKeyPressEvent = event;
+}
+
+function checkResult(event)
+{
+    let currentTest = tests[currentTestIndex];
+    if (lastKeyPressEvent && currentTest.shouldDispatchKeyPress)
+        testPassed(`${currentTest.key} did dispatch a keypress event.`);
+    else if (lastKeyPressEvent)
+        testFailed(`${currentTest.key} should not have dispatched a keypress event. It did dispatch one.`);
+    else if (currentTest.shouldDispatchKeyPress)
+        testFailed(`${currentTest.key} should have dispatched a keypress event. It did not dispatch one.`);
+    else
+        testPassed(`${currentTest.key} did not dispatch a keypress event.`);
+    lastKeyPressEvent = null;
+    nextKeyPress();
+}
+
+function runTest()
+{
+    if (!window.testRunner){
+        testFailed("Must be run in DumpRenderTree/WebKitTestRunner");
+        finishJSTest();
+        return;
+    }
+    document.body.onkeypress = handleKeyPress;
+    document.body.onkeyup = checkResult;
+    nextKeyPress();
+}
+
+runTest();
+</script>
+</body>
+</html>
index fbd2aa8..43c774a 100644 (file)
@@ -307,7 +307,7 @@ window.UIHelper = class UIHelper {
     static typeCharacter(characterString)
     {
         if (!this.isWebKit2() || !this.isIOS()) {
-            eventSender.keyDown(key);
+            eventSender.keyDown(characterString);
             return;
         }
 
index 60529c1..839db75 100644 (file)
@@ -1,3 +1,76 @@
+2018-10-01  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Special keys are misidentified in DOM keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=189974
+
+        Reviewed by Wenson Hsieh.
+
+        This patch fixes two issues:
+            1. Special keyboard keys would be misidentified in dispatched DOM keyboard events.
+            2. DOM keypress events may not be dispatched for some special keys.
+
+        UIKit uses special input strings to identify the Page Up, Page Down, Escape, Up Arrow, Down Arrow,
+        Left Arrow, and Right Arrow keys. It also uses ASCII control characters to represent some other
+        special keys, including Num Lock / Clear, Home, End, Forward Delete, and F1, ..., F24. We need
+        to explicitly handle these special keyboard keys in order to be able to identify the key that
+        was pressed as well as to correctly disambiguate a key down to know whether to dispatch a DOM
+        keypress event for the key.
+
+        Unlike UIKit, AppKit reserves Unicode Private Use Area (PUA) code points in 0xF700–0xF8FF to
+        represent special keyboard keys. This makes it straightforward to disambiguate such keys using
+        the input string of the keyboard event alone. To simplify the implementation for iOS
+        we normalize the input string be AppKit compatible. See the explaination for WebCore::windowsKeyCodeForCharCode()
+        below for more details on why this is done.
+
+        Tests: fast/events/ios/keydown-keyup-arrow-keys-in-non-editable-element.html
+               fast/events/ios/keypress-keys-in-non-editable-element.html
+
+        * SourcesCocoa.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        Do not use unified source build strategy when building WebEvent.mm as it makes
+        use of SoftLinking macros that are incompatible with this strategy.
+
+        * platform/ios/KeyEventIOS.mm:
+        (WebCore::windowsKeyCodeForCharCode): Recognize some special AppKit special char codes.
+        These special char codes are generated by WebKit. WebKit uses the same special char codes
+        as AppKit as a convenience instead of defining our own constants for the same purpose.
+        Encoding the special UIKit input strings (e.g. up arrow) as distinct char codes allows us
+        to use integer arithmetic and switch blocks to map characters to Windows virtual key
+        codes as opposed to special cased branches to perform pointer or string comparisions.
+        The latter would be necessary in Modern WebKit in order for key down events to be properly
+        disambiguated to dispatch a DOM keypress event because pointers are not perserved, though
+        what they point to is, when sending the WebEvent from UIProcess to the WebProcess and
+        vice versa.
+        (WebCore::isFunctionKey): Convenience function that determines whether the specified char
+        code corresponds to a function key on the keyboard. The term "function key" is taken from
+        AppKit parlance to describe a special keyboard key. These keys include F1, F2, ..., F24,
+        and cursor keys among other special keyboard keys.
+        (WebCore::PlatformKeyboardEvent::disambiguateKeyDownEvent): Write in terms of isFunctionKey().
+        * platform/ios/PlatformEventFactoryIOS.h:
+        * platform/ios/PlatformEventFactoryIOS.mm:
+        (WebCore::keyIdentifierForKeyEvent): Remove code to handle UIKit special input strings as
+        we now map such special input strings to char codes and hence can use the default code path.
+        (WebCore::keyForKeyEvent): Ditto.
+        (WebCore::codeForKeyEvent): Remove code to compute the Window virtual key code corresponding
+        to a UIKit special key command now that we map such special input strings to char codes and
+        subsequently map the char codes to the Windows virtual key code (see -[WebEvent initWithKeyEventType:...]
+        constructors). So, we can now use WebEvent.keyCode directly to compute the DOM UIEvents code
+        for the event.
+        (WebCore::PlatformKeyboardEventBuilder::PlatformKeyboardEventBuilder): Remove code to fix up
+        WebEvent.keyCode to account for UIKit special input strings now that we map such special key
+        commands to char codes and subsequently map the char codes to the Windows virtual key code (see -[WebEvent initWithKeyEventType:...]
+        constructors). So, we can now take WebEvent.keyCode verbatim to be the Window virtual key code.
+        (WebCore::convertSpecialKeyToCharCode): Deleted.
+        (WebCore::keyCodeForEvent): Deleted.
+        * platform/ios/WebEvent.mm:
+        (normalizedStringWithAppKitCompatibilityMapping): Added; converts a UIKit character string
+        to the corresponding AppKit-compatible one (if not already compatible). See the explaination
+        for WebCore::windowsKeyCodeForCharCode() above for more details on why this is done.
+
+        (-[WebEvent initWithKeyEventType:timeStamp:characters:charactersIgnoringModifiers:modifiers:isRepeating:withFlags:keyCode:isTabKey:characterSet:]):
+        (-[WebEvent initWithKeyEventType:timeStamp:characters:charactersIgnoringModifiers:modifiers:isRepeating:withFlags:withInputManagerHint:keyCode:isTabKey:]):
+        Normalize the character strings to be AppKit compatible.
+
 2018-10-01  Simon Fraser  <simon.fraser@apple.com>
 
         Optimize RenderStyle::diff() and clean up the code
index f4aaa84..3daccca 100644 (file)
@@ -1,3 +1,15 @@
+2018-10-01  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Special keys are misidentified in DOM keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=189974
+
+        Reviewed by Wenson Hsieh.
+
+        Forward declare or define more SPI.
+
+        * pal/spi/cocoa/IOKitSPI.h:
+        * pal/spi/ios/UIKitSPI.h:
+
 2018-09-25  Jiewen Tan  <jiewen_tan@apple.com>
 
         [WebAuthN] Make AuthenticatorManager
index 3eb1b08..e366014 100644 (file)
@@ -195,6 +195,7 @@ enum {
     kHIDUsage_KeyboardLeftArrow = 0x50,
     kHIDUsage_KeyboardDownArrow = 0x51,
     kHIDUsage_KeyboardUpArrow = 0x52,
+    kHIDUsage_KeypadNumLock = 0x53,
     kHIDUsage_KeyboardF13 = 0x68,
     kHIDUsage_KeyboardMenu = 0x76,
     kHIDUsage_KeyboardLeftControl = 0xE0,
index 9db7f57..5f850d9 100644 (file)
@@ -29,6 +29,7 @@
 #import <UIKit/UIApplication_Private.h>
 #import <UIKit/UIColor_Private.h>
 #import <UIKit/UIInterface_Private.h>
+#import <UIKit/UIResponder_Private.h>
 #import <UIKit/UIScreen_Private.h>
 #import <UIKit/UIViewController_Private.h>
 
@@ -136,4 +137,16 @@ NS_ASSUME_NONNULL_END
 
 #endif
 
+NS_ASSUME_NONNULL_BEGIN
+
+WTF_EXTERN_C_BEGIN
+
+extern NSString *const UIKeyInputPageUp;
+extern NSString *const UIKeyInputPageDown;
+extern NSString *const UIKeyInputEscape;
+
+WTF_EXTERN_C_END
+
+NS_ASSUME_NONNULL_END
+
 #endif
index 032d045..58136a9 100644 (file)
@@ -410,7 +410,7 @@ platform/ios/VideoFullscreenInterfaceAVKit.mm @no-unify
 platform/ios/WebAVPlayerController.mm
 platform/ios/WebBackgroundTaskController.mm
 platform/ios/WebCoreMotionManager.mm
-platform/ios/WebEvent.mm
+platform/ios/WebEvent.mm @no-unify
 platform/ios/WebItemProviderPasteboard.mm @no-unify
 platform/ios/WebSQLiteDatabaseTrackerClient.mm
 platform/ios/WebVideoFullscreenControllerAVKit.mm
index 0b7eb38..5aa39bf 100644 (file)
                CE08C3D2152B599A0021B8C2 /* AlternativeTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = CE08C3D0152B599A0021B8C2 /* AlternativeTextController.h */; settings = {ATTRIBUTES = (); }; };
                CE1866451F72E5B400A0CAB6 /* MarkedText.h in Headers */ = {isa = PBXBuildFile; fileRef = CE1866431F72E5B400A0CAB6 /* MarkedText.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE2849871CA360DF00B4A57F /* ContentSecurityPolicyDirectiveNames.h in Headers */ = {isa = PBXBuildFile; fileRef = CE2849861CA360DF00B4A57F /* ContentSecurityPolicyDirectiveNames.h */; };
+               CE4ECCD6215AA81200558C41 /* WebEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = FE0D84EA1048436E001A179E /* WebEvent.mm */; };
                CE5FA255209E48C50051D700 /* ContentSecurityPolicyClient.h in Headers */ = {isa = PBXBuildFile; fileRef = CE5FA253209E48C50051D700 /* ContentSecurityPolicyClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE6DADFA1C591E6A003F6A88 /* ContentSecurityPolicyResponseHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = CE6DADF81C591E6A003F6A88 /* ContentSecurityPolicyResponseHeaders.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE799F981C6A46BC0097B518 /* ContentSecurityPolicySourceList.h in Headers */ = {isa = PBXBuildFile; fileRef = CE799F961C6A46BC0097B518 /* ContentSecurityPolicySourceList.h */; };
                                CD7E05221651C28200C1201F /* WebCoreAVFResourceLoader.mm in Sources */,
                                CD225C0B1C46FBF400140761 /* WebCoreNSURLSession.mm in Sources */,
                                93F19B0708245E59001E9ABC /* WebCoreView.m in Sources */,
+                               CE4ECCD6215AA81200558C41 /* WebEvent.mm in Sources */,
                                D3F3D36D1A69B7DC0059FC2B /* WebGL2RenderingContext.cpp in Sources */,
                                49C7B9C81042D32F0009D447 /* WebGLBuffer.cpp in Sources */,
                                D08B00E220A282490004BC0A /* WebGLCompressedTextureASTC.cpp in Sources */,
index acb8ba1..3ef664b 100644 (file)
@@ -29,6 +29,7 @@
 #if PLATFORM(IOS)
 
 #import "KeyEventCocoa.h"
+#import "KeyEventCodesIOS.h"
 #import "NotImplemented.h"
 #import "WindowsKeyboardCodes.h"
 #import <pal/spi/cocoa/IOKitSPI.h>
@@ -202,7 +203,7 @@ int windowsKeyCodeForCharCode(unichar charCode)
     case 8: case 0x7F: return VK_BACK;
     case 9: return VK_TAB;
     case 0xD: case 3: return VK_RETURN;
-    case 0x1B: return VK_ESCAPE;
+    case 0x1B: return VK_ESCAPE; // WebKit generated code for Escape.
     case ' ': return VK_SPACE;
 
     case '0': case ')': return VK_0;
@@ -242,6 +243,14 @@ int windowsKeyCodeForCharCode(unichar charCode)
     case 'y': case 'Y': return VK_Y;
     case 'z': case 'Z': return VK_Z;
 
+    // WebKit uses Unicode PUA codes in the OpenStep reserve range for some special keys.
+    case NSUpArrowFunctionKey: return VK_UP;
+    case NSDownArrowFunctionKey: return VK_DOWN;
+    case NSLeftArrowFunctionKey: return VK_LEFT;
+    case NSRightArrowFunctionKey: return VK_RIGHT;
+    case NSPageUpFunctionKey: return VK_PRIOR;
+    case NSPageDownFunctionKey: return VK_NEXT;
+
     // This is for U.S. keyboard mapping, and doesn't necessarily make sense for different keyboard layouts.
     // For example, '"' on Windows Russian layout is VK_2, not VK_OEM_7.
     case ';': case ':': return VK_OEM_1;
@@ -259,6 +268,28 @@ int windowsKeyCodeForCharCode(unichar charCode)
     return 0;
 }
 
+static bool isFunctionKey(UChar charCode)
+{
+    switch (charCode) {
+    case 1: // Home
+    case 4: // End
+    case 5: // FIXME: For some reason WebKitTestRunner generates this code for F14 (why?).
+    case 0x7F: // Forward Delete
+    case 0x10: // Function key (e.g. F1, F2, ...)
+
+    // WebKit uses Unicode PUA codes in the OpenStep reserve range for some special keys.
+    case NSUpArrowFunctionKey:
+    case NSDownArrowFunctionKey:
+    case NSLeftArrowFunctionKey:
+    case NSRightArrowFunctionKey:
+    case NSPageUpFunctionKey:
+    case NSPageDownFunctionKey:
+    case NSClearLineFunctionKey: // Num Lock / Clear
+        return true;
+    }
+    return false;
+}
+
 void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCompatibilityMode)
 {
     // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions.
@@ -274,10 +305,7 @@ void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCom
     } else {
         m_keyIdentifier = String();
         m_windowsVirtualKeyCode = 0;
-        if (m_text.length() == 1 && (m_text[0U] >= 0xF700 && m_text[0U] <= 0xF7FF)) {
-            // According to NSEvents.h, OpenStep reserves the range 0xF700-0xF8FF for function keys. However, some actual private use characters
-            // happen to be in this range, e.g. the Apple logo (Option+Shift+K).
-            // 0xF7FF is an arbitrary cut-off.
+        if (m_text.length() == 1 && isFunctionKey(m_text[0])) {
             m_text = String();
             m_unmodifiedText = String();
         }
index 79a8a66..bc74594 100644 (file)
@@ -53,7 +53,6 @@ public:
 WEBCORE_EXPORT String keyForKeyEvent(WebEvent *);
 WEBCORE_EXPORT String codeForKeyEvent(WebEvent *);
 WEBCORE_EXPORT String keyIdentifierForKeyEvent(WebEvent *);
-WEBCORE_EXPORT int keyCodeForEvent(WebEvent *);
 
 } // namespace WebCore
 
index 380d66f..b860a35 100644 (file)
 #import "WAKAppKitStubs.h"
 #import "WebEvent.h"
 #import "WindowsKeyboardCodes.h"
-#import <UIKit/UIKit.h>
 #import <wtf/Optional.h>
-#import <wtf/SoftLinking.h>
 #import <wtf/WallTime.h>
 
-SOFT_LINK_FRAMEWORK(UIKit)
-SOFT_LINK_CONSTANT(UIKit, UIKeyInputUpArrow, NSString *)
-SOFT_LINK_CONSTANT(UIKit, UIKeyInputDownArrow, NSString *)
-SOFT_LINK_CONSTANT(UIKit, UIKeyInputLeftArrow, NSString *)
-SOFT_LINK_CONSTANT(UIKit, UIKeyInputRightArrow, NSString *)
-
-#define UIKeyInputUpArrow getUIKeyInputUpArrow()
-#define UIKeyInputDownArrow getUIKeyInputDownArrow()
-#define UIKeyInputLeftArrow getUIKeyInputLeftArrow()
-#define UIKeyInputRightArrow getUIKeyInputRightArrow()
-
 namespace WebCore {
 
 static OptionSet<PlatformEvent::Modifier> modifiersForEvent(WebEvent *event)
@@ -97,32 +84,6 @@ static PlatformEvent::Type mouseEventType(WebEvent *event)
     }
 }
 
-static std::optional<int> convertSpecialKeyToCharCode(NSString *keyString)
-{
-    if ([keyString isEqualToString:UIKeyInputUpArrow])
-        return NSUpArrowFunctionKey;
-    if ([keyString isEqualToString:UIKeyInputDownArrow])
-        return NSDownArrowFunctionKey;
-    if ([keyString isEqualToString:UIKeyInputLeftArrow])
-        return NSLeftArrowFunctionKey;
-    if ([keyString isEqualToString:UIKeyInputRightArrow])
-        return NSRightArrowFunctionKey;
-    return std::nullopt;
-}
-
-int keyCodeForEvent(WebEvent *event)
-{
-    if ([event.characters isEqualToString:UIKeyInputUpArrow])
-        return VK_UP;
-    if ([event.characters isEqualToString:UIKeyInputDownArrow])
-        return VK_DOWN;
-    if ([event.characters isEqualToString:UIKeyInputLeftArrow])
-        return VK_LEFT;
-    if ([event.characters isEqualToString:UIKeyInputRightArrow])
-        return VK_RIGHT;
-    return event.keyCode;
-}
-
 class PlatformMouseEventBuilder : public PlatformMouseEvent {
 public:
     PlatformMouseEventBuilder(WebEvent *event)
@@ -167,8 +128,6 @@ PlatformWheelEvent PlatformEventFactory::createPlatformWheelEvent(WebEvent *even
 String keyIdentifierForKeyEvent(WebEvent *event)
 {
     NSString *characters = event.charactersIgnoringModifiers;
-    if (auto specialKeyCharCode = convertSpecialKeyToCharCode(characters))
-        return keyIdentifierForCharCode(*specialKeyCharCode);
     if ([characters length] != 1) {
         LOG(Events, "received an unexpected number of characters in key event: %u", [characters length]);
         return "Unidentified"_s;
@@ -185,8 +144,6 @@ String keyForKeyEvent(WebEvent *event)
     // "Dead" is defined here https://w3c.github.io/uievents-key/#keys-composition.
     if (!length)
         return "Dead"_s;
-    if (auto specialKeyCharCode = convertSpecialKeyToCharCode(characters))
-        return keyForCharCode(*specialKeyCharCode);
     if (length > 1)
         return characters;
     return keyForCharCode([characters characterAtIndex:0]);
@@ -195,7 +152,7 @@ String keyForKeyEvent(WebEvent *event)
 // https://w3c.github.io/uievents-code/
 String codeForKeyEvent(WebEvent *event)
 {
-    switch (keyCodeForEvent(event)) {
+    switch (event.keyCode) {
     // Keys in the alphanumeric section.
     case VK_OEM_3: return "Backquote"_s;
     case VK_OEM_5: return "Backslash"_s;
@@ -419,7 +376,7 @@ public:
         m_key = keyForKeyEvent(event);
         m_code = codeForKeyEvent(event);
         m_keyIdentifier = keyIdentifierForKeyEvent(event);
-        m_windowsVirtualKeyCode = keyCodeForEvent(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;
index dad9a0a..f247386 100644 (file)
 
 #if PLATFORM(IOS)
 
+#import "KeyEventCodesIOS.h"
 #import "WAKAppKitStubs.h"
+#import <pal/spi/ios/UIKitSPI.h>
+#import <wtf/SoftLinking.h>
+
+SOFT_LINK_FRAMEWORK(UIKit)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputUpArrow, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputDownArrow, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputLeftArrow, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputRightArrow, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputPageUp, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputPageDown, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIKeyInputEscape, NSString *)
+
+#define UIKeyInputUpArrow getUIKeyInputUpArrow()
+#define UIKeyInputDownArrow getUIKeyInputDownArrow()
+#define UIKeyInputLeftArrow getUIKeyInputLeftArrow()
+#define UIKeyInputRightArrow getUIKeyInputRightArrow()
+#define UIKeyInputPageUp getUIKeyInputPageUp()
+#define UIKeyInputPageDown getUIKeyInputPageDown()
+#define UIKeyInputEscape getUIKeyInputEscape()
 
 using WebCore::windowsKeyCodeForKeyCode;
 using WebCore::windowsKeyCodeForCharCode;
+
 @implementation WebEvent
 
 @synthesize type = _type;
@@ -121,6 +142,29 @@ static int windowsKeyCodeForCharCodeIOS(unichar charCode)
     return windowsKeyCodeForCharCode(charCode);
 }
 
+static NSString* normalizedStringWithAppKitCompatibilityMapping(NSString *characters)
+{
+    auto makeNSStringWithCharacter = [] (unichar c) { return [NSString stringWithCharacters:&c length:1]; };
+
+    if (characters == UIKeyInputUpArrow)
+        return makeNSStringWithCharacter(NSUpArrowFunctionKey);
+    if (characters == UIKeyInputDownArrow)
+        return makeNSStringWithCharacter(NSDownArrowFunctionKey);
+    if (characters == UIKeyInputLeftArrow)
+        return makeNSStringWithCharacter(NSLeftArrowFunctionKey);
+    if (characters == UIKeyInputRightArrow)
+        return makeNSStringWithCharacter(NSRightArrowFunctionKey);
+    if (characters == UIKeyInputPageUp)
+        return makeNSStringWithCharacter(NSPageUpFunctionKey);
+    if (characters == UIKeyInputPageDown)
+        return makeNSStringWithCharacter(NSPageDownFunctionKey);
+    if (characters == UIKeyInputEscape)
+        return @"\x1B";
+    if ([characters isEqualToString:@"\x1B"]) // Num Lock / Clear
+        return makeNSStringWithCharacter(NSClearLineFunctionKey);
+    return characters;
+}
+
 // FIXME: to be removed when the adoption of the new initializer is complete.
 - (WebEvent *)initWithKeyEventType:(WebEventType)type
                          timeStamp:(CFTimeInterval)timeStamp
@@ -141,8 +185,8 @@ static int windowsKeyCodeForCharCodeIOS(unichar charCode)
     _type = type;
     _timestamp = timeStamp;
 
-    _characters = [characters retain];
-    _charactersIgnoringModifiers = [charactersIgnoringModifiers retain];
+    _characters = [normalizedStringWithAppKitCompatibilityMapping(characters) retain];
+    _charactersIgnoringModifiers = [normalizedStringWithAppKitCompatibilityMapping(charactersIgnoringModifiers) retain];
     _modifierFlags = modifiers;
     _keyRepeating = repeating;
     _keyboardFlags = flags;
@@ -176,8 +220,8 @@ static int windowsKeyCodeForCharCodeIOS(unichar charCode)
     _type = type;
     _timestamp = timeStamp;
     
-    _characters = [characters retain];
-    _charactersIgnoringModifiers = [charactersIgnoringModifiers retain];
+    _characters = [normalizedStringWithAppKitCompatibilityMapping(characters) retain];
+    _charactersIgnoringModifiers = [normalizedStringWithAppKitCompatibilityMapping(charactersIgnoringModifiers) retain];
     _modifierFlags = modifiers;
     _keyRepeating = repeating;
     _keyboardFlags = flags;
index 2f4ab08..9711361 100644 (file)
@@ -1,3 +1,17 @@
+2018-10-01  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Special keys are misidentified in DOM keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=189974
+
+        Reviewed by Wenson Hsieh.
+
+        Take the key code of WebEvent to be the key code for the new WebKeyboardEvent verbatim
+        now that we normalize the character strings of the WebEvent to account for the special
+        UIKit input strings.
+
+        * Shared/ios/WebIOSEventFactory.mm:
+        (WebIOSEventFactory::createWebKeyboardEvent):
+
 2018-10-01  Alex Christensen  <achristensen@webkit.org>
 
         Fix an internal build after r236665
index 832fd14..d414557 100644 (file)
@@ -58,8 +58,9 @@ WebKit::WebKeyboardEvent WebIOSEventFactory::createWebKeyboardEvent(::WebEvent *
     String key = WebCore::keyForKeyEvent(event);
     String code = WebCore::codeForKeyEvent(event);
     String keyIdentifier = WebCore::keyIdentifierForKeyEvent(event);
-    int windowsVirtualKeyCode = WebCore::keyCodeForEvent(event);
-    int nativeVirtualKeyCode = WebCore::keyCodeForEvent(event);
+    int windowsVirtualKeyCode = event.keyCode;
+    // FIXME: This is not correct. WebEvent.keyCode represents the Windows native virtual key code.
+    int nativeVirtualKeyCode = event.keyCode;
     int macCharCode = 0;
     bool autoRepeat = event.isKeyRepeating;
     bool isKeypad = false;
index b554c39..421bae4 100644 (file)
@@ -1,3 +1,15 @@
+2018-10-01  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Special keys are misidentified in DOM keyboard events
+        https://bugs.webkit.org/show_bug.cgi?id=189974
+
+        Reviewed by Wenson Hsieh.
+
+        Add support for testing keys Forward Delete and Num Lock / Clear.
+
+        * WebKitTestRunner/ios/HIDEventGenerator.mm:
+        (hidUsageCodeForCharacter):
+
 2018-10-01  Alex Christensen  <achristensen@webkit.org>
 
         URL should not use TextEncoding internally
index 0815bdf..935615f 100644 (file)
@@ -952,10 +952,14 @@ static inline uint32_t hidUsageCodeForCharacter(NSString *key)
         return kHIDUsage_KeyboardDownArrow;
     if ([key isEqualToString:@"delete"])
         return kHIDUsage_KeyboardDeleteOrBackspace;
+    if ([key isEqualToString:@"forwardDelete"])
+        return kHIDUsage_KeyboardDeleteForward;
     if ([key isEqualToString:@"leftCommand"])
         return kHIDUsage_KeyboardLeftGUI;
     if ([key isEqualToString:@"rightCommand"])
         return kHIDUsage_KeyboardRightGUI;
+    if ([key isEqualToString:@"clear"]) // Num Lock / Clear
+        return kHIDUsage_KeypadNumLock;
     // The simulator keyboard interprets both left and right modifier keys using the left version of the usage code.
     if ([key isEqualToString:@"leftControl"] || [key isEqualToString:@"rightControl"])
         return kHIDUsage_KeyboardLeftControl;